From d37e8734201eae082c30e88bd18dd286ac964f31 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 15 Jul 2026 12:51:39 -0700 Subject: [PATCH] refactor(plugins): split plugin type contracts (#108416) * refactor(plugins): split plugin type contracts * refactor(plugins): split provider type contracts * refactor(plugins): extract plugin definition contract --- config/max-lines-baseline.txt | 1 - .../.generated/plugin-sdk-api-baseline.sha256 | 4 +- src/plugins/capability-provider.types.ts | 202 ++ src/plugins/migration-provider.types.ts | 137 + src/plugins/plugin-api.types.ts | 484 +++ src/plugins/plugin-command.types.ts | 194 ++ src/plugins/plugin-config-schema.types.ts | 27 + src/plugins/plugin-definition.types.ts | 31 + src/plugins/plugin-registration.types.ts | 305 ++ src/plugins/provider-authentication.types.ts | 237 ++ src/plugins/provider-catalog.types.ts | 137 + src/plugins/provider-plugin.types.ts | 693 ++++ src/plugins/provider-replay.types.ts | 123 + src/plugins/provider-runtime.types.ts | 303 ++ src/plugins/provider-transport.types.ts | 184 + src/plugins/types.ts | 3043 +---------------- 16 files changed, 3130 insertions(+), 2975 deletions(-) create mode 100644 src/plugins/capability-provider.types.ts create mode 100644 src/plugins/migration-provider.types.ts create mode 100644 src/plugins/plugin-api.types.ts create mode 100644 src/plugins/plugin-command.types.ts create mode 100644 src/plugins/plugin-config-schema.types.ts create mode 100644 src/plugins/plugin-definition.types.ts create mode 100644 src/plugins/plugin-registration.types.ts create mode 100644 src/plugins/provider-authentication.types.ts create mode 100644 src/plugins/provider-catalog.types.ts create mode 100644 src/plugins/provider-plugin.types.ts create mode 100644 src/plugins/provider-replay.types.ts create mode 100644 src/plugins/provider-runtime.types.ts create mode 100644 src/plugins/provider-transport.types.ts diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt index 7f1842b152a1..b47318d0355f 100644 --- a/config/max-lines-baseline.txt +++ b/config/max-lines-baseline.txt @@ -1062,7 +1062,6 @@ src/plugins/setup-registry.test.ts src/plugins/setup-registry.ts src/plugins/tools.optional.test.ts src/plugins/tools.ts -src/plugins/types.ts src/plugins/uninstall.test.ts src/plugins/update.test.ts src/plugins/update.ts diff --git a/docs/.generated/plugin-sdk-api-baseline.sha256 b/docs/.generated/plugin-sdk-api-baseline.sha256 index 81ecc1cae969..9ad4cd704a42 100644 --- a/docs/.generated/plugin-sdk-api-baseline.sha256 +++ b/docs/.generated/plugin-sdk-api-baseline.sha256 @@ -1,2 +1,2 @@ -96059a79ccb739f0618f450663a0b956abfd122599d390b28cdd173acc3cc08c plugin-sdk-api-baseline.json -7a80f7d3e10613b5aa86264ceb2b6b70fadc92f4736faf0743761a6e8c46d8eb plugin-sdk-api-baseline.jsonl +066904c4d04ef88d161bb0f8e0bb4e4ee8ecfc629f13b0efc0e49a12fcaef230 plugin-sdk-api-baseline.json +70827c20207ef0042de00e53604f170d60dd5ac1a24acee05ea81932bdef145e plugin-sdk-api-baseline.jsonl diff --git a/src/plugins/capability-provider.types.ts b/src/plugins/capability-provider.types.ts new file mode 100644 index 000000000000..1cd81ca07dbf --- /dev/null +++ b/src/plugins/capability-provider.types.ts @@ -0,0 +1,202 @@ +import type { SecretRef } from "../config/types.secrets.js"; +import type { ImageGenerationProvider } from "../image-generation/types.js"; +import type { MediaUnderstandingProvider } from "../media-understanding/types.js"; +import type { MusicGenerationProvider } from "../music-generation/types.js"; +import type { + RealtimeTranscriptionProviderConfig, + RealtimeTranscriptionProviderConfiguredContext, + RealtimeTranscriptionProviderId, + RealtimeTranscriptionProviderResolveConfigContext, + RealtimeTranscriptionSession, + RealtimeTranscriptionSessionCreateRequest, +} from "../realtime-transcription/provider-types.js"; +import type { + RealtimeVoiceBridge, + RealtimeVoiceBrowserSession, + RealtimeVoiceBrowserSessionCreateRequest, + RealtimeVoiceBridgeCreateRequest, + RealtimeVoiceProviderCapabilities, + RealtimeVoiceProviderConfig, + RealtimeVoiceProviderConfiguredContext, + RealtimeVoiceProviderId, + RealtimeVoiceProviderResolveConfigContext, +} from "../talk/provider-types.js"; +import type { TranscriptSourceProvider as TranscriptsSourceProviderCapability } from "../transcripts/provider-types.js"; +import type { + SpeechDirectiveTokenParseContext, + SpeechDirectiveTokenParseResult, + SpeechProviderConfiguredContext, + SpeechProviderConfig, + SpeechProviderResolveConfigContext, + SpeechProviderResolveTalkConfigContext, + SpeechProviderResolveTalkOverridesContext, + SpeechListVoicesRequest, + SpeechProviderPrepareSynthesisContext, + SpeechProviderPreparedSynthesis, + SpeechProviderId, + SpeechSynthesisRequest, + SpeechSynthesisResult, + SpeechSynthesisStreamRequest, + SpeechSynthesisStreamResult, + SpeechTelephonySynthesisRequest, + SpeechTelephonySynthesisResult, + SpeechVoiceOption, +} from "../tts/provider-types.js"; +import type { VideoGenerationProvider } from "../video-generation/types.js"; +import type { PluginJsonValue } from "./host-hooks.js"; + +/** JSON-compatible provider settings for one configured worker profile. */ +export type WorkerProfile = Readonly>; + +/** SSH endpoint material returned by a worker provider after provisioning. */ +export type WorkerSshEndpoint = { + host: string; + port: number; + user: string; + /** OpenSSH public host-key line obtained from trusted provisioning output. */ + hostKey: string; + /** Secret reference only; providers must never return plaintext key material. */ + keyRef: SecretRef; +}; + +/** Resolved SSH client identity. Providers may return a local path or ephemeral material. */ +export type WorkerSshIdentity = + | { kind: "path"; path: string } + | { kind: "material"; contents: string }; + +/** Durable context supplied when a worker provider resolves the identity it minted. */ +export type WorkerSshIdentityRequest = { + leaseId: string; + profile: WorkerProfile; + keyRef: SecretRef; +}; + +/** Durable lease identity and endpoint returned by a successful provision operation. */ +export type WorkerLease = { + leaseId: string; + ssh: WorkerSshEndpoint; +}; + +/** Authoritative inspection result for an already-known worker lease. */ +export type WorkerLeaseStatus = + | { status: "active" } + | { status: "destroyed" } + | { status: "unknown" }; + +/** Permanent provider rejection recorded as a terminal worker failure. */ +export class WorkerProviderError extends Error { + readonly code = "invalid_profile"; + + constructor(message: string) { + super(message); + this.name = "WorkerProviderError"; + } +} + +/** Cloud-worker lifecycle capability registered by a plugin. */ +export type WorkerProvider = { + id: string; + /** + * Provision or adopt the lease for this operation id. + * Repeating the same operation id must be idempotent across gateway restarts. + */ + provision: (profile: WorkerProfile, operationId: string) => Promise; + /** Throws on transient/indeterminate failures; `unknown` means authoritative absence. */ + inspect: (lease: { leaseId: string; profile: WorkerProfile }) => Promise; + /** + * Resolves provider-owned dynamic identities. When absent, the gateway uses its generic + * SecretRef resolver; when present, failures are authoritative and never fall back. + */ + resolveSshIdentity?: (request: WorkerSshIdentityRequest) => Promise; + renew?: (leaseId: string) => Promise; + /** Idempotent; resolves only after the provider can prove teardown. */ + destroy: (lease: { leaseId: string; profile: WorkerProfile }) => Promise; +}; + +/** Speech capability registered by a plugin. */ +export type SpeechProviderPlugin = { + id: SpeechProviderId; + label: string; + aliases?: string[]; + autoSelectOrder?: number; + /** Default provider operation timeout in milliseconds when caller/config omit timeoutMs. */ + defaultTimeoutMs?: number; + defaultModel?: string; + models?: readonly string[]; + voices?: readonly string[]; + resolveConfig?: (ctx: SpeechProviderResolveConfigContext) => SpeechProviderConfig; + parseDirectiveToken?: (ctx: SpeechDirectiveTokenParseContext) => SpeechDirectiveTokenParseResult; + resolveTalkConfig?: (ctx: SpeechProviderResolveTalkConfigContext) => SpeechProviderConfig; + resolveTalkOverrides?: ( + ctx: SpeechProviderResolveTalkOverridesContext, + ) => SpeechProviderConfig | undefined; + prepareSynthesis?: ( + ctx: SpeechProviderPrepareSynthesisContext, + ) => + | SpeechProviderPreparedSynthesis + | undefined + | Promise; + isConfigured: (ctx: SpeechProviderConfiguredContext) => boolean; + synthesize: (req: SpeechSynthesisRequest) => Promise; + streamSynthesize?: (req: SpeechSynthesisStreamRequest) => Promise; + synthesizeTelephony?: ( + req: SpeechTelephonySynthesisRequest, + ) => Promise; + listVoices?: (req: SpeechListVoicesRequest) => Promise; +}; + +export type PluginSpeechProviderEntry = SpeechProviderPlugin & { + pluginId: string; +}; + +/** Realtime transcription capability registered by a plugin. */ +export type RealtimeTranscriptionProviderPlugin = { + id: RealtimeTranscriptionProviderId; + label: string; + aliases?: string[]; + defaultModel?: string; + models?: readonly string[]; + autoSelectOrder?: number; + resolveConfig?: ( + ctx: RealtimeTranscriptionProviderResolveConfigContext, + ) => RealtimeTranscriptionProviderConfig; + isConfigured: (ctx: RealtimeTranscriptionProviderConfiguredContext) => boolean; + createSession: (req: RealtimeTranscriptionSessionCreateRequest) => RealtimeTranscriptionSession; +}; + +export type PluginRealtimeTranscriptionProviderEntry = RealtimeTranscriptionProviderPlugin & { + pluginId: string; +}; + +/** Transcript source capability registered by a channel or meeting plugin. */ +export type TranscriptSourceProvider = TranscriptsSourceProviderCapability; + +export type PluginTranscriptsSourceProviderEntry = TranscriptSourceProvider & { + pluginId: string; +}; + +/** Realtime voice capability registered by a plugin. */ +export type RealtimeVoiceProviderPlugin = { + id: RealtimeVoiceProviderId; + label: string; + aliases?: string[]; + defaultModel?: string; + models?: readonly string[]; + autoSelectOrder?: number; + capabilities?: RealtimeVoiceProviderCapabilities; + resolveConfig?: (ctx: RealtimeVoiceProviderResolveConfigContext) => RealtimeVoiceProviderConfig; + isConfigured: (ctx: RealtimeVoiceProviderConfiguredContext) => boolean; + createBridge: (req: RealtimeVoiceBridgeCreateRequest) => RealtimeVoiceBridge; + createBrowserSession?: ( + req: RealtimeVoiceBrowserSessionCreateRequest, + ) => Promise; +}; + +export type PluginRealtimeVoiceProviderEntry = RealtimeVoiceProviderPlugin & { + pluginId: string; +}; + +export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider; +export type ImageGenerationProviderPlugin = ImageGenerationProvider; +export type VideoGenerationProviderPlugin = VideoGenerationProvider; +export type MusicGenerationProviderPlugin = MusicGenerationProvider; diff --git a/src/plugins/migration-provider.types.ts b/src/plugins/migration-provider.types.ts new file mode 100644 index 000000000000..9ef68341bbc4 --- /dev/null +++ b/src/plugins/migration-provider.types.ts @@ -0,0 +1,137 @@ +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { PluginLogger } from "./logger-types.js"; +import type { PluginRuntime } from "./runtime/types.js"; + +export type PluginConfigMigration = (config: OpenClawConfig) => + | { + config: OpenClawConfig; + changes: string[]; + } + | null + | undefined; + +export type MigrationItemStatus = + | "planned" + | "migrated" + | "skipped" + | "warning" + | "conflict" + | "error"; +export type MigrationItemKind = + | "auth" + | "config" + | "secret" + | "memory" + | "skill" + | "workspace" + | "session" + | "file" + | "archive" + | "manual"; +export type MigrationItemAction = + | "copy" + | "create" + | "update" + | "merge" + | "append" + | "archive" + | "skip" + | "manual"; + +export type MigrationItem = { + id: string; + kind: MigrationItemKind | (string & {}); + action: MigrationItemAction | (string & {}); + status: MigrationItemStatus; + source?: string; + target?: string; + message?: string; + reason?: string; + sensitive?: boolean; + /** Core-owned source revision bound by reviewed embedded migration flows. */ + sourceRevision?: { algorithm: "sha256"; digest: string }; + details?: Record; +}; + +export type MigrationSummary = { + total: number; + planned: number; + migrated: number; + skipped: number; + conflicts: number; + errors: number; + sensitive: number; +}; + +export type MigrationDetection = { + found: boolean; + source?: string; + label?: string; + confidence?: "low" | "medium" | "high"; + message?: string; +}; + +export type MigrationPlan = { + providerId: string; + source: string; + target?: string; + summary: MigrationSummary; + items: MigrationItem[]; + warnings?: string[]; + nextSteps?: string[]; + metadata?: Record; +}; + +export type MigrationApplyResult = MigrationPlan & { + backupPath?: string; + reportDir?: string; +}; + +export type MigrationProviderPreparation = { + dispose?: () => void | Promise; +}; + +export type MigrationProviderContext = { + config: OpenClawConfig; + runtime?: PluginRuntime; + logger: PluginLogger; + stateDir: string; + /** Explicit destination agent for embedded migration surfaces such as Control UI. */ + targetAgentId?: string; + /** Optional item-kind scope used by embedded migration surfaces to avoid unrelated discovery. */ + itemKinds?: readonly string[]; + source?: string; + includeSecrets?: boolean; + overwrite?: boolean; + providerOptions?: Record; + backupPath?: string; + reportDir?: string; + signal?: AbortSignal; +}; + +/** Migration source implemented by a plugin and orchestrated by `openclaw migrate`. */ +export type MigrationProviderPlugin = { + id: string; + label: string; + description?: string; + /** Item kinds this provider can expose without requiring a full plan. */ + supportedItemKinds?: readonly string[]; + detect?: (ctx: MigrationProviderContext) => MigrationDetection | Promise; + prepareApply?: ( + ctx: MigrationProviderContext, + ) => MigrationProviderPreparation | Promise | undefined; + plan: (ctx: MigrationProviderContext) => MigrationPlan | Promise; + apply: ( + ctx: MigrationProviderContext, + plan?: MigrationPlan, + ) => MigrationApplyResult | Promise; +}; + +export type PluginSetupAutoEnableContext = { + config: OpenClawConfig; + env: NodeJS.ProcessEnv; +}; + +export type PluginSetupAutoEnableProbe = ( + ctx: PluginSetupAutoEnableContext, +) => string | string[] | null | undefined; diff --git a/src/plugins/plugin-api.types.ts b/src/plugins/plugin-api.types.ts new file mode 100644 index 000000000000..55309176627a --- /dev/null +++ b/src/plugins/plugin-api.types.ts @@ -0,0 +1,484 @@ +import type { AgentHarness } from "../agents/harness/types.js"; +import type { AnyAgentTool } from "../agents/tools/common.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { OperatorScope } from "../gateway/operator-scopes.js"; +import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; +import type { InternalHookHandler } from "../hooks/internal-hook-types.js"; +import type { + AgentToolResultMiddleware, + AgentToolResultMiddlewareOptions, +} from "./agent-tool-result-middleware-types.js"; +import type { + ImageGenerationProviderPlugin, + MediaUnderstandingProviderPlugin, + MusicGenerationProviderPlugin, + RealtimeTranscriptionProviderPlugin, + RealtimeVoiceProviderPlugin, + SpeechProviderPlugin, + TranscriptSourceProvider, + VideoGenerationProviderPlugin, + WorkerProvider, +} from "./capability-provider.types.js"; +import type { CliBackendPlugin, PluginTextTransforms } from "./cli-backend.types.js"; +import type { CodexAppServerExtensionFactory } from "./codex-app-server-extension-types.js"; +import type { PluginConversationBindingResolvedEvent } from "./conversation-binding.types.js"; +import type { PluginHookHandlerMap, PluginHookName } from "./hook-types.js"; +import type { + PluginAgentEventEmitParams, + PluginAgentEventEmitResult, + PluginAgentEventSubscriptionRegistration, + PluginControlUiDescriptor, + PluginJsonValue, + PluginNextTurnInjection, + PluginNextTurnInjectionEnqueueResult, + PluginRunContextGetParams, + PluginRunContextPatch, + PluginRuntimeLifecycleRegistration, + PluginSessionActionRegistration, + PluginSessionAttachmentParams, + PluginSessionAttachmentResult, + PluginSessionSchedulerJobHandle, + PluginSessionSchedulerJobRegistration, + PluginSessionExtensionRegistration, + PluginSessionTurnScheduleParams, + PluginSessionTurnUnscheduleByTagParams, + PluginSessionTurnUnscheduleByTagResult, + PluginToolMetadataRegistration, + PluginTrustedToolPolicyRegistration, +} from "./host-hooks.js"; +import type { PluginLogger } from "./logger-types.js"; +import type { + MigrationProviderPlugin, + PluginConfigMigration, + PluginSetupAutoEnableProbe, +} from "./migration-provider.types.js"; +import type { OpenClawPluginCommandDefinition } from "./plugin-command.types.js"; +import type { + OpenClawPluginChannelRegistration, + OpenClawPluginCliCommandDescriptor, + OpenClawPluginCliRegistrar, + OpenClawGatewayDiscoveryService, + OpenClawPluginHostedMediaResolver, + OpenClawPluginHttpRouteParams, + OpenClawPluginNodeCliFeatureOptions, + OpenClawPluginNodeInvokePolicy, + OpenClawPluginReloadRegistration, + OpenClawPluginSecurityAuditCollector, + OpenClawPluginService, + PluginInteractiveHandlerRegistration, + PluginRegistrationMode, +} from "./plugin-registration.types.js"; +import type { UnifiedModelCatalogProviderPlugin } from "./provider-catalog.types.js"; +import type { ProviderPlugin } from "./provider-plugin.types.js"; +import type { PluginRuntime } from "./runtime/types.js"; +import type { SessionCatalogProvider } from "./session-catalog.js"; +import type { + OpenClawPluginHookOptions, + OpenClawPluginToolFactory, + OpenClawPluginToolOptions, +} from "./tool-types.js"; +import type { OpenClawPluginNodeHostCommand } from "./types.node-host.js"; +import type { WebFetchProviderPlugin, WebSearchProviderPlugin } from "./web-provider-types.js"; + +type ChannelPlugin = import("../channels/plugins/types.plugin.js").ChannelPlugin; + +export type PluginTextTransformRegistration = PluginTextTransforms; + +export type OpenClawPluginSessionStateApi = { + /** Register plugin-owned session state projected into Gateway session rows. */ + registerSessionExtension: (extension: PluginSessionExtensionRegistration) => void; +}; + +export type OpenClawPluginSessionWorkflowApi = { + /** Queue one plugin-owned context injection for the next agent turn in a session. */ + enqueueNextTurnInjection: ( + injection: PluginNextTurnInjection, + ) => Promise; + /** + * Register cleanup metadata for a plugin-owned session scheduler job. + * This does not schedule work or create task records; it only lets the host + * clean external scheduler state during reset/delete/disable. + */ + registerSessionSchedulerJob: ( + job: PluginSessionSchedulerJobRegistration, + ) => PluginSessionSchedulerJobHandle | undefined; + /** Send host-validated files to the active direct-outbound route for a session. */ + sendSessionAttachment: ( + params: PluginSessionAttachmentParams, + ) => Promise; + /** + * Schedule a future agent turn in a session through Cron. + * Cron owns timing and creates the task ledger entry when the turn runs. + */ + scheduleSessionTurn: ( + params: PluginSessionTurnScheduleParams, + ) => Promise; + /** Remove Cron-backed scheduled session turns that share a plugin-owned tag. */ + unscheduleSessionTurnsByTag: ( + params: PluginSessionTurnUnscheduleByTagParams, + ) => Promise; +}; + +export type OpenClawPluginSessionControlsApi = { + /** Register a typed session action that clients can dispatch through the Gateway. */ + registerSessionAction: (action: PluginSessionActionRegistration) => void; + /** Register a generic Control UI contribution descriptor. */ + registerControlUiDescriptor: (descriptor: PluginControlUiDescriptor) => void; +}; + +export type OpenClawPluginSessionApi = { + state: OpenClawPluginSessionStateApi; + workflow: OpenClawPluginSessionWorkflowApi; + controls: OpenClawPluginSessionControlsApi; +}; + +export type OpenClawPluginAgentEventsApi = { + /** Subscribe to sanitized agent events through the host-owned plugin lifecycle. */ + registerAgentEventSubscription: (subscription: PluginAgentEventSubscriptionRegistration) => void; + /** Emit a host-routed, plugin-attributed event for workflow/UI subscribers. */ + emitAgentEvent: (params: PluginAgentEventEmitParams) => PluginAgentEventEmitResult; +}; + +export type OpenClawPluginAgentApi = { + events: OpenClawPluginAgentEventsApi; +}; + +export type OpenClawPluginRunContextApi = { + /** Store namespaced, JSON-compatible data for the active run. Cleared on run end/error. */ + setRunContext: (patch: PluginRunContextPatch) => boolean; + /** Read namespaced plugin data for a run. */ + getRunContext: (params: PluginRunContextGetParams) => PluginJsonValue | undefined; + /** Clear one namespace or all namespaces this plugin owns for a run. */ + clearRunContext: (params: { runId: string; namespace?: string }) => void; +}; + +export type OpenClawPluginLifecycleApi = { + /** Register cleanup hooks for plugin-owned host state and background work. */ + registerRuntimeLifecycle: (lifecycle: PluginRuntimeLifecycleRegistration) => void; +}; + +/** Main registration API injected into native plugin entry files. */ +export type OpenClawPluginApi = { + id: string; + name: string; + version?: string; + description?: string; + source: string; + rootDir?: string; + registrationMode: PluginRegistrationMode; + config: OpenClawConfig; + pluginConfig?: Record; + /** + * In-process runtime helpers for trusted native plugins. + * + * This surface is broader than hooks. Prefer hooks for third-party + * automation/integration unless you need native registry integration. + */ + runtime: PluginRuntime; + logger: PluginLogger; + /** + * Grouped facade over the existing flat session-related plugin API. + * Flat methods remain supported for compatibility. + */ + session: OpenClawPluginSessionApi; + /** Grouped facade for agent-event workflow seams. */ + agent: OpenClawPluginAgentApi; + /** Grouped facade for run-scoped plugin scratch state. */ + runContext: OpenClawPluginRunContextApi; + /** Grouped facade for plugin-owned lifecycle cleanup hooks. */ + lifecycle: OpenClawPluginLifecycleApi; + registerTool: ( + tool: AnyAgentTool | OpenClawPluginToolFactory, + opts?: OpenClawPluginToolOptions, + ) => void; + registerHook: ( + events: string | string[], + handler: InternalHookHandler, + opts?: OpenClawPluginHookOptions, + ) => void; + registerHttpRoute: (params: OpenClawPluginHttpRouteParams) => void; + /** Register a plugin-owned resolver for browser-style hosted media URLs. */ + registerHostedMediaResolver: (resolver: OpenClawPluginHostedMediaResolver) => void; + /** Bind a declared MCP server's transport to the trusted message requester. */ registerMcpServerConnectionResolver: ( + resolver: import("./types.mcp-connection.js").OpenClawPluginMcpServerConnectionResolver, + ) => void; + /** Register a native messaging channel plugin (channel capability). */ + registerChannel: (registration: OpenClawPluginChannelRegistration | ChannelPlugin) => void; + /** + * Register a gateway RPC method for this plugin. + * + * Reserved core admin namespaces (`config.*`, `exec.approvals.*`, + * `wizard.*`, `update.*`) always normalize to `operator.admin` even if a + * narrower scope is requested. + */ + registerGatewayMethod: ( + method: string, + handler: GatewayRequestHandler, + opts?: { scope?: OperatorScope }, + ) => void; + /** Register a read-only external-session catalog with optional native adoption actions. */ + registerSessionCatalog: (provider: SessionCatalogProvider) => void; + registerCli: ( + registrar: OpenClawPluginCliRegistrar, + opts?: { + /** Parent command path for nested command groups, for example `["nodes"]`. */ + parentPath?: string[]; + /** Explicit command names owned by this registrar at `parentPath`. */ + commands?: string[]; + /** + * Parse-time command descriptors for lazy CLI registration. + * + * When descriptors cover every command exposed at `parentPath`, OpenClaw + * can keep the plugin registrar lazy. Command-only registrations stay on + * the eager compatibility path. + */ + descriptors?: OpenClawPluginCliCommandDescriptor[]; + }, + ) => void; + /** + * Register a plugin-owned node feature command group under `openclaw nodes`. + * + * This is equivalent to `registerCli(registrar, { parentPath: ["nodes"], ... })` + * and is intended for paired-node capabilities such as camera, screen, or Canvas. + */ + registerNodeCliFeature: ( + registrar: OpenClawPluginCliRegistrar, + opts?: OpenClawPluginNodeCliFeatureOptions, + ) => void; + registerReload: (registration: OpenClawPluginReloadRegistration) => void; + registerNodeHostCommand: (command: OpenClawPluginNodeHostCommand) => void; + registerNodeInvokePolicy: (policy: OpenClawPluginNodeInvokePolicy) => void; + registerSecurityAuditCollector: (collector: OpenClawPluginSecurityAuditCollector) => void; + registerService: (service: OpenClawPluginService) => void; + /** Register a local gateway discovery advertiser such as mDNS/Bonjour. */ + registerGatewayDiscoveryService: (service: OpenClawGatewayDiscoveryService) => void; + /** Register a text-only CLI backend used by the local CLI runner. */ + registerCliBackend: (backend: CliBackendPlugin) => void; + /** Register plugin-owned prompt/message compatibility text transforms. */ + registerTextTransforms: (transforms: PluginTextTransformRegistration) => void; + /** Register a lightweight config migration that can run before plugin runtime loads. */ + registerConfigMigration: (migrate: PluginConfigMigration) => void; + /** Register an importer for `openclaw migrate` (migration capability). */ + registerMigrationProvider: (provider: MigrationProviderPlugin) => void; + /** Register a lightweight config probe that can auto-enable this plugin generically. */ + registerAutoEnableProbe: (probe: PluginSetupAutoEnableProbe) => void; + /** Register a native model/provider plugin (text inference capability). */ + registerProvider: (provider: ProviderPlugin) => void; + /** Register a cloud-worker lifecycle provider. */ + registerWorkerProvider: (provider: WorkerProvider) => void; + /** Register provider-owned model catalog rows for text and media generation. */ + registerModelCatalogProvider: (provider: UnifiedModelCatalogProviderPlugin) => void; + /** Register a general embedding provider (embedding capability). */ + registerEmbeddingProvider: ( + adapter: import("./embedding-providers.js").EmbeddingProviderAdapter, + ) => void; + /** Register a speech synthesis provider (speech capability). */ + registerSpeechProvider: (provider: SpeechProviderPlugin) => void; + /** Register a realtime transcription provider (streaming STT capability). */ + registerRealtimeTranscriptionProvider: (provider: RealtimeTranscriptionProviderPlugin) => void; + /** Register a realtime voice provider (duplex voice capability). */ + registerRealtimeVoiceProvider: (provider: RealtimeVoiceProviderPlugin) => void; + /** Register a media understanding provider (media understanding capability). */ + registerMediaUnderstandingProvider: (provider: MediaUnderstandingProviderPlugin) => void; + /** Register a transcripts source provider (live or imported meeting transcript capability). */ + registerTranscriptSourceProvider: (provider: TranscriptSourceProvider) => void; + /** Register an image generation provider (image generation capability). */ + registerImageGenerationProvider: (provider: ImageGenerationProviderPlugin) => void; + /** Register a video generation provider (video generation capability). */ + registerVideoGenerationProvider: (provider: VideoGenerationProviderPlugin) => void; + /** Register a music generation provider (music generation capability). */ + registerMusicGenerationProvider: (provider: MusicGenerationProviderPlugin) => void; + /** Register a web fetch provider (web fetch capability). */ + registerWebFetchProvider: (provider: WebFetchProviderPlugin) => void; + /** Register a web search provider (web search capability). */ + registerWebSearchProvider: (provider: WebSearchProviderPlugin) => void; + registerInteractiveHandler: (registration: PluginInteractiveHandlerRegistration) => void; + onConversationBindingResolved: ( + handler: (event: PluginConversationBindingResolvedEvent) => void | Promise, + ) => void; + /** + * Register a custom command that bypasses the LLM agent. + * Plugin commands are processed before built-in commands and before agent invocation. + * Use this for simple state-toggling or status commands that don't need AI reasoning. + */ + registerCommand: (command: OpenClawPluginCommandDefinition) => void; + /** Register a context engine implementation (exclusive slot - only one active at a time). */ + registerContextEngine: ( + id: string, + factory: import("../context-engine/registry.js").ContextEngineFactory, + ) => void; + /** Register a compaction provider (pluggable summarization backend). */ + registerCompactionProvider: ( + provider: import("./compaction-provider.js").CompactionProvider, + ) => void; + /** Register an agent harness implementation. */ + registerAgentHarness: (harness: AgentHarness) => void; + /** + * Register a Codex app-server extension factory for Codex harness tool-result + * middleware. Only bundled plugins may use this seam, and + * `contracts.embeddedExtensionFactories` must include `"codex-app-server"`. + */ + registerCodexAppServerExtensionFactory: (factory: CodexAppServerExtensionFactory) => void; + /** + * Register runtime-neutral tool-result middleware. Declare + * `contracts.agentToolResultMiddleware` for every targeted runtime. + */ + registerAgentToolResultMiddleware: ( + handler: AgentToolResultMiddleware, + options?: AgentToolResultMiddlewareOptions, + ) => void; + /** + * Register plugin-owned session state that can be projected into Gateway session rows. + * @deprecated Use `api.session.state.registerSessionExtension(...)`. + */ + registerSessionExtension: (extension: PluginSessionExtensionRegistration) => void; + /** + * Queue one plugin-owned context injection for the next agent turn in a session. + * @deprecated Use `api.session.workflow.enqueueNextTurnInjection(...)`. + */ + enqueueNextTurnInjection: ( + injection: PluginNextTurnInjection, + ) => Promise; + /** + * Register a trusted pre-tool policy. Installed plugins must declare the + * policy id in `contracts.trustedToolPolicies`. + */ + registerTrustedToolPolicy: (policy: PluginTrustedToolPolicyRegistration) => void; + /** + * Register display/policy metadata for a plugin-owned tool. Metadata is + * scoped to the (pluginId, toolName) pair at projection time, so plugins + * cannot decorate other plugins' tools or core tools through this surface. + */ + registerToolMetadata: (metadata: PluginToolMetadataRegistration) => void; + /** + * Register a generic Control UI contribution descriptor. + * @deprecated Use `api.session.controls.registerControlUiDescriptor(...)`. + */ + registerControlUiDescriptor: (descriptor: PluginControlUiDescriptor) => void; + /** + * Register cleanup hooks for plugin-owned host state and background work. + * @deprecated Use `api.lifecycle.registerRuntimeLifecycle(...)`. + */ + registerRuntimeLifecycle: (lifecycle: PluginRuntimeLifecycleRegistration) => void; + /** + * Subscribe to sanitized agent events through the host-owned plugin lifecycle. + * @deprecated Use `api.agent.events.registerAgentEventSubscription(...)`. + */ + registerAgentEventSubscription: (subscription: PluginAgentEventSubscriptionRegistration) => void; + /** + * Emit a host-routed, plugin-attributed agent event for workflow/UI subscribers. + * @deprecated Use `api.agent.events.emitAgentEvent(...)`. + */ + emitAgentEvent: (params: PluginAgentEventEmitParams) => PluginAgentEventEmitResult; + /** + * Store namespaced, JSON-compatible data for the active run. Cleared on run end/error. + * @deprecated Use `api.runContext.setRunContext(...)`. + */ + setRunContext: (patch: PluginRunContextPatch) => boolean; + /** + * Read namespaced plugin data for a run. + * @deprecated Use `api.runContext.getRunContext(...)`. + */ + getRunContext: (params: PluginRunContextGetParams) => PluginJsonValue | undefined; + /** + * Clear one namespace or all namespaces this plugin owns for a run. + * @deprecated Use `api.runContext.clearRunContext(...)`. + */ + clearRunContext: (params: { runId: string; namespace?: string }) => void; + /** + * Register cleanup metadata for a plugin-owned session scheduler job. + * This does not schedule work or create task records; it only lets the host + * clean external scheduler state during reset/delete/disable. + * + * @deprecated Use `api.session.workflow.registerSessionSchedulerJob(...)`. + */ + registerSessionSchedulerJob: ( + job: PluginSessionSchedulerJobRegistration, + ) => PluginSessionSchedulerJobHandle | undefined; + /** + * Register a typed session action that clients can dispatch through the Gateway. + * @deprecated Use `api.session.controls.registerSessionAction(...)`. + */ + registerSessionAction: (action: PluginSessionActionRegistration) => void; + /** + * Send one or more host-validated files to the active direct-outbound channel for a session. + * + * This API is intended for bundled plugins running with the host channel/session + * integration available. Calls may resolve to `{ ok: false }` instead of attaching + * files when global side effects are disabled or when the required plugin/channel + * runtime is not loaded, so callers must handle rejection via the returned result. + * + * @deprecated Use `api.session.workflow.sendSessionAttachment(...)`. + */ + sendSessionAttachment: ( + params: PluginSessionAttachmentParams, + ) => Promise; + /** + * Schedule a future agent turn in a session through Cron. + * Cron owns timing and creates the task ledger entry when the turn runs. + * Bundled plugins only; workspace plugins receive undefined. + * + * @deprecated Use `api.session.workflow.scheduleSessionTurn(...)`. + */ + scheduleSessionTurn: ( + params: PluginSessionTurnScheduleParams, + ) => Promise; + /** + * Remove Cron-backed scheduled session turns that share the same plugin-owned tag. + * Bundled plugins only; workspace plugins receive a zero-count result. + * + * @deprecated Use `api.session.workflow.unscheduleSessionTurnsByTag(...)`. + */ + unscheduleSessionTurnsByTag: ( + params: PluginSessionTurnUnscheduleByTagParams, + ) => Promise; + /** Register the active detached task runtime for this plugin (exclusive slot). */ + registerDetachedTaskRuntime: ( + runtime: import("./runtime/runtime-tasks.types.js").DetachedTaskLifecycleRuntime, + ) => void; + /** Register the active memory capability for this memory plugin (exclusive slot). */ + registerMemoryCapability: ( + capability: import("./memory-state.js").MemoryPluginCapability, + ) => void; + /** + * Register the system prompt section builder for this memory plugin (exclusive slot). + * @deprecated Use registerMemoryCapability({ promptBuilder }) instead. + */ + registerMemoryPromptSection: ( + builder: import("./memory-state.js").MemoryPromptSectionBuilder, + ) => void; + /** Register an additive memory-adjacent prompt section (non-exclusive). */ + registerMemoryPromptSupplement: ( + builder: import("./memory-state.js").MemoryPromptSectionBuilder, + ) => void; + /** Register an additive memory-adjacent search/read corpus supplement (non-exclusive). */ + registerMemoryCorpusSupplement: ( + supplement: import("./memory-state.js").MemoryCorpusSupplement, + ) => void; + /** + * Register the pre-compaction flush plan resolver for this memory plugin (exclusive slot). + * @deprecated Use registerMemoryCapability({ flushPlanResolver }) instead. + */ + registerMemoryFlushPlan: (resolver: import("./memory-state.js").MemoryFlushPlanResolver) => void; + /** + * Register the active memory runtime adapter for this memory plugin (exclusive slot). + * @deprecated Use registerMemoryCapability({ runtime }) instead. + */ + registerMemoryRuntime: (runtime: import("./memory-state.js").MemoryPluginRuntime) => void; + /** + * Register a memory embedding provider adapter. Multiple adapters may coexist. + * @deprecated New embedding providers should use `registerEmbeddingProvider` + * and `contracts.embeddingProviders`. This memory-specific seam is retained + * while existing memory providers migrate. + */ + registerMemoryEmbeddingProvider: ( + adapter: import("./memory-embedding-providers.js").MemoryEmbeddingProviderAdapter, + ) => void; + resolvePath: (input: string) => string; + /** Register a lifecycle hook handler */ + on: ( + hookName: K, + handler: PluginHookHandlerMap[K], + opts?: { priority?: number; timeoutMs?: number }, + ) => void; +}; diff --git a/src/plugins/plugin-command.types.ts b/src/plugins/plugin-command.types.ts new file mode 100644 index 000000000000..4cab081e2fb3 --- /dev/null +++ b/src/plugins/plugin-command.types.ts @@ -0,0 +1,194 @@ +import type { ReplyPayload } from "../auto-reply/reply-payload.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { OperatorScope } from "../gateway/operator-scopes.js"; +import type { + PluginConversationBinding, + PluginConversationBindingRequestParams, + PluginConversationBindingRequestResult, +} from "./conversation-binding.types.js"; + +type ChannelId = import("../channels/plugins/types.core.js").ChannelId; + +// ============================================================================= +// Plugin Commands +// ============================================================================= + +export type PluginCommandDiagnosticsSession = { + /** Stable host session key when available. */ + sessionKey?: string; + /** Ephemeral OpenClaw session id when available. */ + sessionId?: string; + /** + * Deprecated transcript locator for this OpenClaw session when available. + * + * SQLite-backed sessions use a `sqlite:::` + * marker, not a filesystem path. Use session id/key plus transcript-runtime + * helpers for active transcript reads. + * + * @deprecated Use session identity fields with `plugin-sdk/session-transcript-runtime`. + */ + sessionFile?: string; + /** Embedded agent harness selected for this session. */ + agentHarnessId?: string; + /** Channel/provider for this session when available. */ + channel?: string; + /** Provider channel id when available. */ + channelId?: ChannelId; + /** Account id for multi-account channels when available. */ + accountId?: string; + /** Thread/topic id when available. */ + messageThreadId?: string | number; + /** Parent conversation id for thread-capable channels when available. */ + threadParentId?: string; +}; + +/** + * Context passed to plugin command handlers. + */ +export type PluginCommandContext = { + /** The sender's identifier (for example a channel-scoped user ID) */ + senderId?: string; + /** The channel/surface (for example "chat" or "team-chat") */ + channel: string; + /** Provider channel id */ + channelId?: ChannelId; + /** Whether the sender is on the allowlist */ + isAuthorizedSender: boolean; + /** Whether the sender is an owner for owner-only command surfaces. */ + senderIsOwner?: boolean; + /** Gateway client scopes for internal control-plane callers */ + gatewayClientScopes?: string[]; + /** Host-resolved agent that owns the active session. */ + agentId?: string; + /** Stable host session key for the active conversation when available. */ + sessionKey?: string; + /** Ephemeral host session id for the active conversation when available. */ + sessionId?: string; + /** + * Deprecated transcript locator for the active OpenClaw session when available. + * + * SQLite-backed sessions use a `sqlite:::` + * marker, not a filesystem path. Use session id/key plus transcript-runtime + * helpers for active transcript reads. + * + * @deprecated Use session identity fields with `plugin-sdk/session-transcript-runtime`. + */ + sessionFile?: string; + /** Raw command arguments after the command name */ + args?: string; + /** The full normalized command body */ + commandBody: string; + /** Current OpenClaw configuration */ + config: OpenClawConfig; + /** Raw "From" value (channel-scoped id) */ + from?: string; + /** Raw "To" value (channel-scoped id) */ + to?: string; + /** Account id for multi-account channels */ + accountId?: string; + /** Thread/topic id if available */ + messageThreadId?: string | number; + /** Parent conversation id for thread-capable channels */ + threadParentId?: string; + /** Sensitive diagnostics-only session inventory for owner-gated commands. */ + diagnosticsSessions?: PluginCommandDiagnosticsSession[]; + /** Host-bound runtime capabilities scoped to this command invocation. */ + runtimeContext?: { + llm?: Pick; + }; + /** Internal diagnostics-only marker that exec approval already authorized upload. */ + diagnosticsUploadApproved?: boolean; + /** Internal diagnostics-only marker to preview upload effects without exposing ids. */ + diagnosticsPreviewOnly?: boolean; + /** Internal diagnostics-only marker for owner-private routed confirmations. */ + diagnosticsPrivateRouted?: boolean; + requestConversationBinding: ( + params?: PluginConversationBindingRequestParams, + ) => Promise; + detachConversationBinding: () => Promise<{ removed: boolean }>; + getCurrentConversationBinding: () => Promise; +}; + +/** + * Result returned by a plugin command handler. + */ +export type PluginCommandResult = ReplyPayload & { + /** Allows the agent session to continue processing after the command. */ + continueAgent?: boolean; + /** Suppresses channel fallback replies when the handler already delivered a response. */ + suppressReply?: boolean; +}; + +/** + * Handler function for plugin commands. + */ +export type PluginCommandHandler = ( + ctx: PluginCommandContext, +) => PluginCommandResult | Promise; + +/** + * Definition for a plugin-registered command. + */ +export const AGENT_PROMPT_SURFACE_KINDS = [ + "openclaw_main", + /** @deprecated Use openclaw_main. */ + "pi_main", + "codex_app_server", + "cli_backend", + "acp_backend", + "subagent", +] as const; + +export type AgentPromptSurfaceKind = (typeof AGENT_PROMPT_SURFACE_KINDS)[number]; + +export type AgentPromptGuidanceEntry = { + text: string; + surfaces?: readonly AgentPromptSurfaceKind[]; +}; + +export type AgentPromptGuidance = string | AgentPromptGuidanceEntry; + +export type OpenClawPluginCommandDefinition = { + /** Command name without leading slash (e.g., "tts") */ + name: string; + /** + * Optional native-command aliases for slash/menu surfaces. + * `default` applies to all native providers unless a provider-specific + * override exists (for example `{ default: "talkvoice", teamChat: "voice2" }`). + */ + nativeNames?: Partial> & { default?: string }; + /** + * Optional native progress placeholder text for native command surfaces. + * `default` applies to all native providers unless a provider-specific + * override exists. + */ + nativeProgressMessages?: Partial> & { + default?: string; + }; + /** Description shown in /help and command menus */ + description: string; + /** Localized descriptions for native command surfaces that support them. */ + descriptionLocalizations?: Record; + /** + * Optional channel ids this command belongs to. + * Omit to keep the command available on every channel surface. + */ + channels?: readonly string[]; + /** Optional system-prompt guidance for agents when this command is registered. */ + agentPromptGuidance?: readonly AgentPromptGuidance[]; + /** Whether this command accepts arguments */ + acceptsArgs?: boolean; + /** Whether only authorized senders can use this command (default: true) */ + requireAuth?: boolean; + /** Operator scopes required by gateway clients; command owners may satisfy this on chat surfaces. */ + requiredScopes?: OperatorScope[]; + /** Whether a trusted bundled handler needs owner status for subcommand-level authorization. */ + exposeSenderIsOwner?: boolean; + /** + * Allows a bundled plugin to claim a command name that is otherwise reserved + * by core. External plugins cannot use this field. + */ + ownership?: "plugin" | "reserved"; + /** The handler function */ + handler: PluginCommandHandler; +}; diff --git a/src/plugins/plugin-config-schema.types.ts b/src/plugins/plugin-config-schema.types.ts new file mode 100644 index 000000000000..b61f88777cfc --- /dev/null +++ b/src/plugins/plugin-config-schema.types.ts @@ -0,0 +1,27 @@ +import type { JsonSchemaObject } from "../shared/json-schema.types.js"; +import type { PluginConfigUiHint } from "./manifest-types.js"; + +export type PluginConfigValidation = + | { ok: true; value?: unknown } + | { ok: false; errors: string[] }; + +/** + * Config schema contract accepted by plugin manifests and runtime registration. + * + * Plugins can provide a Zod-like parser, a lightweight `validate(...)` + * function, or both. `uiHints` and `jsonSchema` are optional extras for docs, + * forms, and config UIs. + */ +export type OpenClawPluginConfigSchema = { + safeParse?: (value: unknown) => { + success: boolean; + data?: unknown; + error?: { + issues?: Array<{ path: Array; message: string }>; + }; + }; + parse?: (value: unknown) => unknown; + validate?: (value: unknown) => PluginConfigValidation; + uiHints?: Record; + jsonSchema?: JsonSchemaObject; +}; diff --git a/src/plugins/plugin-definition.types.ts b/src/plugins/plugin-definition.types.ts new file mode 100644 index 000000000000..b2e6bfc9213d --- /dev/null +++ b/src/plugins/plugin-definition.types.ts @@ -0,0 +1,31 @@ +import type { OpenClawPluginApi } from "./plugin-api.types.js"; +import type { OpenClawPluginConfigSchema } from "./plugin-config-schema.types.js"; +import type { PluginKind } from "./plugin-kind.types.js"; +import type { + OpenClawPluginReloadRegistration, + OpenClawPluginSecurityAuditCollector, +} from "./plugin-registration.types.js"; +import type { OpenClawPluginNodeHostCommand } from "./types.node-host.js"; + +/** Module-level plugin definition loaded from a native plugin entry file. */ +export type OpenClawPluginDefinition = { + id?: string; + name?: string; + description?: string; + version?: string; + /** + * @deprecated Declare exclusive plugin kind in `openclaw.plugin.json` via + * manifest `kind`. Runtime-exported `kind` is kept as a compatibility + * fallback for older plugins and may require loading plugin runtime on + * metadata-only command paths. + */ + kind?: PluginKind | PluginKind[]; + configSchema?: OpenClawPluginConfigSchema; + reload?: OpenClawPluginReloadRegistration; + nodeHostCommands?: OpenClawPluginNodeHostCommand[]; + securityAuditCollectors?: OpenClawPluginSecurityAuditCollector[]; + register?: (api: OpenClawPluginApi) => void; + activate?: (api: OpenClawPluginApi) => void; +}; + +export type OpenClawPluginModule = OpenClawPluginDefinition | ((api: OpenClawPluginApi) => void); diff --git a/src/plugins/plugin-registration.types.ts b/src/plugins/plugin-registration.types.ts new file mode 100644 index 000000000000..111d0b1353ab --- /dev/null +++ b/src/plugins/plugin-registration.types.ts @@ -0,0 +1,305 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; +import type { Duplex } from "node:stream"; +import type { Command } from "commander"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; +import type { + DiagnosticEventPrivateData, + DiagnosticEventInput, + DiagnosticEventMetadata, + DiagnosticEventPayload, +} from "../infra/diagnostic-events.js"; +import type { SecurityAuditFinding } from "../security/audit.types.js"; +import type { PluginLogger } from "./logger-types.js"; + +type ChannelPlugin = import("../channels/plugins/types.plugin.js").ChannelPlugin; + +export type OpenClawPluginGatewayMethod = { + method: string; + handler: GatewayRequestHandler; +}; + +export type PluginInteractiveHandlerResult = { + handled?: boolean; +} | void; + +export type PluginInteractiveRegistration< + TContext = unknown, + TChannel extends string = string, + TResult = PluginInteractiveHandlerResult, +> = { + channel: TChannel; + namespace: string; + handler: (ctx: TContext) => Promise | TResult; +}; + +export type PluginInteractiveHandlerRegistration = PluginInteractiveRegistration; + +export type OpenClawPluginHttpRouteAuth = "gateway" | "plugin"; +export type OpenClawPluginHttpRouteMatch = "exact" | "prefix"; +export type OpenClawPluginGatewayRuntimeScopeSurface = "write-default" | "trusted-operator"; + +export type OpenClawPluginHttpRouteHandler = ( + req: IncomingMessage, + res: ServerResponse, +) => Promise | boolean | void; + +export type OpenClawPluginHttpRouteUpgradeHandler = ( + req: IncomingMessage, + socket: Duplex, + head: Buffer, +) => Promise | boolean | void; + +export type OpenClawPluginHttpRouteParams = { + path: string; + handler: OpenClawPluginHttpRouteHandler; + handleUpgrade?: OpenClawPluginHttpRouteUpgradeHandler; + auth: OpenClawPluginHttpRouteAuth; + match?: OpenClawPluginHttpRouteMatch; + gatewayRuntimeScopeSurface?: OpenClawPluginGatewayRuntimeScopeSurface; + nodeCapability?: { + surface: string; + ttlMs?: number; + }; + replaceExisting?: boolean; +}; + +export type OpenClawPluginHostedMediaResolver = ( + mediaUrl: string, +) => string | null | undefined | Promise; + +export type OpenClawPluginCliContext = { + /** + * Command object where this plugin should register its commands. + * + * For root CLI registrations this is the root `openclaw` program. For nested + * registrations it is the resolved parent command from `parentPath`. + */ + program: Command; + parentPath: readonly string[]; + config: OpenClawConfig; + workspaceDir?: string; + logger: PluginLogger; +}; + +export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise; + +/** + * Top-level CLI metadata for plugin-owned commands. + * + * Descriptors are the parse-time contract for lazy plugin CLI registration. + * If you want OpenClaw to keep a plugin command lazy-loaded while still + * advertising it at the root CLI level, provide descriptors that cover every + * top-level command root registered by that plugin CLI surface. + */ +export type OpenClawPluginCliCommandDescriptor = { + name: string; + description: string; + hasSubcommands: boolean; +}; + +export type OpenClawPluginNodeCliFeatureOptions = { + /** Explicit node feature command names owned under `openclaw nodes`. */ + commands?: string[]; + /** + * Parse-time command descriptors for lazy node feature CLI registration. + * + * Descriptors are registered under `openclaw nodes`, so a descriptor named + * `"camera"` exposes `openclaw nodes camera`. + */ + descriptors?: OpenClawPluginCliCommandDescriptor[]; +}; + +export type OpenClawPluginReloadRegistration = { + restartPrefixes?: string[]; + hotPrefixes?: string[]; + noopPrefixes?: string[]; +}; + +export type { + OpenClawPluginNodeHostCommand, + OpenClawPluginNodeHostCommandAvailabilityContext, + OpenClawPluginNodeHostCommandIo, +} from "./types.node-host.js"; + +export type OpenClawPluginNodeInvokeTransportResult = + | { + ok: true; + payload?: unknown; + payloadJSON?: string | null; + } + | { + ok: false; + code?: string; + message: string; + details?: Record; + }; + +export type OpenClawPluginNodeInvokeApprovalDecision = "allow-once" | "allow-always" | "deny"; + +export type OpenClawPluginNodeInvokePolicyApprovalRuntime = { + request: (input: { + title: string; + description: string; + severity?: "info" | "warning" | "critical"; + toolName?: string; + toolCallId?: string; + agentId?: string; + sessionKey?: string; + timeoutMs?: number; + }) => Promise<{ + id?: string; + decision?: OpenClawPluginNodeInvokeApprovalDecision | null; + }>; +}; + +export type OpenClawPluginNodeInvokePolicyContext = { + nodeId: string; + command: string; + params: unknown; + timeoutMs?: number; + idempotencyKey?: string; + config: OpenClawConfig; + pluginConfig?: Record; + node?: { + nodeId: string; + displayName?: string; + platform?: string; + deviceFamily?: string; + commands?: string[]; + }; + client?: { + connId?: string; + scopes?: string[]; + } | null; + approvals?: OpenClawPluginNodeInvokePolicyApprovalRuntime; + invokeNode: (input?: { + params?: unknown; + timeoutMs?: number; + idempotencyKey?: string; + }) => Promise; +}; + +export type OpenClawPluginNodeInvokePolicyResult = + | { + ok: true; + payload?: unknown; + payloadJSON?: string | null; + } + | { + ok: false; + message: string; + code?: string; + details?: Record; + unavailable?: boolean; + }; + +export type OpenClawPluginNodeInvokePolicy = { + commands: string[]; + /** + * Platforms where these node-handled commands should be allowlisted by default. + * Omit for commands that require explicit `gateway.nodes.allowCommands`. + */ + defaultPlatforms?: Array<"ios" | "android" | "macos" | "windows" | "linux" | "unknown">; + /** + * Dangerous policy commands are filtered out of default allowlists unless + * explicitly allowed by config. + */ + dangerous?: boolean; + /** + * iOS foreground-restricted commands should be queued for foreground delivery + * when an iOS node reports BACKGROUND_UNAVAILABLE. + */ + foregroundRestrictedOnIos?: boolean; + handle: ( + ctx: OpenClawPluginNodeInvokePolicyContext, + ) => Promise | OpenClawPluginNodeInvokePolicyResult; +}; + +export type OpenClawPluginSecurityAuditContext = { + config: OpenClawConfig; + sourceConfig: OpenClawConfig; + env: NodeJS.ProcessEnv; + stateDir: string; + configPath: string; +}; + +export type OpenClawPluginSecurityAuditCollector = ( + ctx: OpenClawPluginSecurityAuditContext, +) => SecurityAuditFinding[] | Promise; + +export type OpenClawGatewayDiscoveryAdvertiseContext = { + machineDisplayName: string; + gatewayPort: number; + gatewayTlsEnabled: boolean; + gatewayTlsFingerprintSha256?: string; + gatewayDirectReachable: boolean; + canvasPort?: number; + tailnetDns?: string; + sshPort?: number; + cliPath?: string; + minimal: boolean; +}; + +export type OpenClawGatewayDiscoveryService = { + id: string; + advertise: ( + ctx: OpenClawGatewayDiscoveryAdvertiseContext, + ) => void | Promise void | Promise }>; +}; + +/** Context passed to long-lived plugin services. */ +export type OpenClawPluginServiceContext = { + config: OpenClawConfig; + workspaceDir?: string; + stateDir: string; + logger: PluginLogger; + gatewayEvents?: import("./gateway-events.js").OpenClawPluginGatewayEvents; + startupTrace?: { + detail?: (name: string, metrics: ReadonlyArray) => void; + measure: (name: string, run: () => T | Promise) => Promise; + }; + internalDiagnostics?: { + emit: (event: DiagnosticEventInput, privateData?: DiagnosticEventPrivateData) => void; + onEvent: ( + listener: ( + event: DiagnosticEventPayload, + metadata: DiagnosticEventMetadata, + privateData: DiagnosticEventPrivateData, + ) => void, + ) => () => void; + }; +}; + +/** Background service registered by a plugin during `register(api)`. */ +export type OpenClawPluginService = { + id: string; + start: (ctx: OpenClawPluginServiceContext) => void | Promise; + stop?: (ctx: OpenClawPluginServiceContext) => void | Promise; +}; + +export type OpenClawPluginChannelRegistration = { + plugin: ChannelPlugin; +}; + +/** + * Public label exposed to plugin `register(api)` calls. + * + * Keep this as a compatibility signal for plugin authors. Loader internals + * should derive explicit capability booleans from the mode instead of branching + * on raw strings throughout the code path. + * + * - `full`: live runtime activation; long-lived side effects may start. + * - `discovery`: read-only capability discovery; skip sockets/workers/clients. + * - `tool-discovery`: capability discovery for executable tools; skip channel runtime hydration. + * - `setup-only`: lightweight channel setup entry only. + * - `setup-runtime`: setup flow that also needs the runtime channel entry. + * - `cli-metadata`: CLI command metadata collection. + */ +export type PluginRegistrationMode = + | "full" + | "discovery" + | "tool-discovery" + | "setup-only" + | "setup-runtime" + | "cli-metadata"; diff --git a/src/plugins/provider-authentication.types.ts b/src/plugins/provider-authentication.types.ts new file mode 100644 index 000000000000..1ce93e0cf2ab --- /dev/null +++ b/src/plugins/provider-authentication.types.ts @@ -0,0 +1,237 @@ +import type { ApiKeyCredential, AuthProfileCredential } from "../agents/auth-profiles/types.js"; +import type { PromptMode } from "../agents/system-prompt.types.js"; +import type { ModelProviderConfig } from "../config/types.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { RuntimeEnv } from "../runtime.js"; +import type { WizardPrompter } from "../wizard/prompts.js"; +import type { SecretInputMode } from "./provider-auth-types.js"; +import type { ProviderAuthOptionBag } from "./provider-external-auth.types.js"; +import type { createVpsAwareOAuthHandlers } from "./provider-oauth-flow.js"; + +export type ProviderAuthKind = "oauth" | "api_key" | "token" | "device_code" | "custom"; + +/** Standard result payload returned by provider auth methods. */ +export type ProviderAuthResult = { + profiles: Array<{ profileId: string; credential: AuthProfileCredential }>; + /** + * Optional config patch to merge after credentials are written. + * + * Use this for provider-owned onboarding defaults such as + * `models.providers.` entries, default aliases, or agent model helpers. + * The caller still persists auth-profile bindings separately. + */ + configPatch?: Partial; + defaultModel?: string; + notes?: string[]; + /** + * Opt in to replace `agents.defaults.models` wholesale with the patch map. + * Default behavior merges the map so other providers' entries survive. + * Set only from migrations that intentionally rename/remove model keys. + */ + replaceDefaultModels?: boolean; +}; + +/** Interactive auth context passed to provider login/setup methods. */ +export type ProviderAuthContext = { + config: OpenClawConfig; + env?: NodeJS.ProcessEnv; + agentDir?: string; + workspaceDir?: string; + prompter: WizardPrompter; + runtime: RuntimeEnv; + /** Cancels browser callbacks, device polling, and other app-owned auth work. */ + signal?: AbortSignal; + /** + * Optional onboarding CLI options that triggered this auth flow. + * + * Present for setup/configure/auth-choice flows so provider methods can + * honor preseeded flags like `--openai-api-key` or generic + * `--token/--token-provider` pairs. Direct `models auth login` usually + * leaves this undefined. + */ + opts?: ProviderAuthOptionBag; + /** + * Onboarding secret persistence preference. + * + * Interactive wizard flows set this when the caller explicitly requested + * plaintext or env/file/exec ref storage. Ad-hoc `models auth login` flows + * usually leave it undefined. + */ + secretInputMode?: SecretInputMode; + /** + * Whether the provider auth flow should offer the onboarding secret-storage + * mode picker when `secretInputMode` is unset. + * + * This is true for onboarding/configure flows and false for direct + * `models auth` commands, which should keep a tighter, provider-owned prompt + * surface. + */ + allowSecretRefPrompt?: boolean; + isRemote: boolean; + openUrl: (url: string) => Promise; + oauth: { + createVpsAwareHandlers: typeof createVpsAwareOAuthHandlers; + }; +}; + +export type ProviderNonInteractiveApiKeyResult = { + key: string; + source: "profile" | "env" | "flag"; + envVarName?: string; +}; + +export type ProviderResolveNonInteractiveApiKeyParams = { + provider: string; + flagValue?: string; + flagName: `--${string}`; + envVar: string; + envVarName?: string; + allowProfile?: boolean; + required?: boolean; +}; + +export type ProviderNonInteractiveApiKeyCredentialParams = { + provider: string; + resolved: ProviderNonInteractiveApiKeyResult; + email?: string; + metadata?: Record; +}; + +export type ProviderAuthMethodNonInteractiveContext = { + authChoice: string; + config: OpenClawConfig; + baseConfig: OpenClawConfig; + opts: ProviderAuthOptionBag; + runtime: RuntimeEnv; + agentDir?: string; + workspaceDir?: string; + resolveApiKey: ( + params: ProviderResolveNonInteractiveApiKeyParams, + ) => Promise; + toApiKeyCredential: ( + params: ProviderNonInteractiveApiKeyCredentialParams, + ) => ApiKeyCredential | null; +}; + +export type ProviderAuthMethod = { + id: string; + label: string; + hint?: string; + kind: ProviderAuthKind; + /** Provider-owned model used to validate app-guided secret setup. */ + starterModel?: string; + /** + * Optional wizard/onboarding metadata for this specific auth method. + * + * Use this when one provider exposes multiple setup entries (for example API + * key + OAuth, or region-specific login flows). OpenClaw uses this to expose + * method-specific auth choices while keeping the provider id stable. + */ + wizard?: ProviderPluginWizardSetup; + run: (ctx: ProviderAuthContext) => Promise; + runNonInteractive?: ( + ctx: ProviderAuthMethodNonInteractiveContext, + ) => Promise; +}; + +export type ProviderPluginWizardSetup = { + choiceId?: string; + choiceLabel?: string; + choiceHint?: string; + assistantPriority?: number; + assistantVisibility?: "visible" | "manual-only"; + onboardingFeatured?: boolean; + groupId?: string; + groupLabel?: string; + groupHint?: string; + methodId?: string; + /** + * Interactive onboarding surfaces where this auth choice should appear. + * Defaults to `["text-inference"]` when omitted. + */ + onboardingScopes?: Array<"text-inference" | "image-generation" | "music-generation">; + /** + * Optional model-allowlist prompt policy applied after this auth choice is + * selected in configure/onboarding flows. + * + * Keep this UI-facing and static. Provider logic that needs runtime state + * should stay in `run`/`runNonInteractive`. + */ + modelAllowlist?: { + allowedKeys?: string[]; + initialSelections?: string[]; + loadCatalog?: boolean; + message?: string; + }; + /** + * Optional default-model prompt policy for this auth/setup choice. + * + * Use this when selecting the auth choice should still force a model picker + * even if the choice was preseeded via CLI/configure, or when "keep current" + * would skip required provider-owned post-selection work. + */ + modelSelection?: { + promptWhenAuthChoiceProvided?: boolean; + allowKeepCurrent?: boolean; + }; +}; + +/** Optional model-picker metadata shown in interactive provider selection flows. */ +export type ProviderPluginWizardModelPicker = { + label?: string; + hint?: string; + methodId?: string; +}; + +/** UI metadata that lets provider plugins appear in onboarding and configure flows. */ +export type ProviderPluginWizard = { + setup?: ProviderPluginWizardSetup; + modelPicker?: ProviderPluginWizardModelPicker; +}; + +export type ProviderOAuthProfileIdRepair = { + /** + * Legacy OAuth profile id to migrate away from. + * + * When omitted, OpenClaw falls back to `:default`. + */ + legacyProfileId?: string; + /** + * Optional custom doctor prompt label. + * + * Defaults to the provider label when omitted. + */ + promptLabel?: string; +}; + +export type ProviderModelSelectedContext = { + config: OpenClawConfig; + model: string; + prompter: WizardPrompter; + agentDir?: string; + workspaceDir?: string; +}; + +export type ProviderDeferSyntheticProfileAuthContext = { + config?: OpenClawConfig; + provider: string; + providerConfig?: ModelProviderConfig; + resolvedApiKey?: string; +}; + +export type ProviderSystemPromptContributionContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; + promptMode: PromptMode; + runtimeChannel?: string; + runtimeCapabilities?: string[]; + agentId?: string; + trigger?: "cron" | "heartbeat" | "manual" | "memory" | "overflow" | "user"; +}; + +export type ProviderTransformSystemPromptContext = ProviderSystemPromptContributionContext & { + systemPrompt: string; +}; diff --git a/src/plugins/provider-catalog.types.ts b/src/plugins/provider-catalog.types.ts new file mode 100644 index 000000000000..2b172f204277 --- /dev/null +++ b/src/plugins/provider-catalog.types.ts @@ -0,0 +1,137 @@ +import type { + UnifiedModelCatalogEntry, + UnifiedModelCatalogKind, +} from "@openclaw/model-catalog-core/model-catalog-types"; +import type { ModelCatalogEntry } from "../agents/model-catalog.types.js"; +import type { ModelProviderConfig } from "../config/types.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; + +export type ProviderCatalogOrder = "simple" | "profile" | "paired" | "late"; + +export type ProviderCatalogContext = { + config: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + resolveProviderApiKey: (providerId?: string) => { + apiKey: string | undefined; + discoveryApiKey?: string; + }; + resolveProviderAuth: ( + providerId?: string, + options?: { + oauthMarker?: string; + }, + ) => { + apiKey: string | undefined; + discoveryApiKey?: string; + mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none"; + source: "env" | "profile" | "none"; + profileId?: string; + }; +}; + +export type ProviderCatalogResult = + | { provider: ModelProviderConfig } + | { providers: Record } + | null + | undefined; + +export type ProviderPluginCatalog = { + order?: ProviderCatalogOrder; + run: (ctx: ProviderCatalogContext) => Promise; +}; + +export type UnifiedModelCatalogProviderContext = ProviderCatalogContext & { + signal?: AbortSignal; + includeLive?: boolean; + timeoutMs?: number; +}; + +export type UnifiedModelCatalogProviderPlugin = { + provider: string; + kinds: readonly UnifiedModelCatalogKind[]; + staticCatalog?: ( + ctx: UnifiedModelCatalogProviderContext, + ) => + | readonly UnifiedModelCatalogEntry[] + | Promise + | null + | undefined; + liveCatalog?: ( + ctx: UnifiedModelCatalogProviderContext, + ) => + | readonly UnifiedModelCatalogEntry[] + | Promise + | null + | undefined; +}; + +/** + * Built-in model suppression hook context. + * + * @deprecated Use manifest `modelCatalog.suppressions`. Runtime suppression + * hooks are no longer called by model resolution. + */ +export type ProviderBuiltInModelSuppressionContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + modelId: string; + baseUrl?: string; +}; + +export type ProviderBuiltInModelSuppressionResult = { + suppress: boolean; + errorMessage?: string; +}; + +/** + * Provider-owned "modern model" policy input. + * + * Live smoke/model-profile selection uses this to keep provider-specific + * inclusion/exclusion rules out of core. + */ +export type ProviderModernModelPolicyContext = { + provider: string; + modelId: string; +}; + +/** + * Final catalog augmentation hook. + * + * Runs after OpenClaw loads the discovered model catalog and merges configured + * opt-in providers. Use this for forward-compat rows or vendor-owned synthetic + * entries that should appear in `models list` and model pickers even when the + * upstream registry has not caught up yet. + */ +export type ProviderAugmentModelCatalogContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + resolveProviderApiKey?: ProviderCatalogContext["resolveProviderApiKey"]; + entries: ModelCatalogEntry[]; +}; + +/** + * @deprecated Use ProviderCatalogOrder. + */ +export type ProviderDiscoveryOrder = ProviderCatalogOrder; + +/** + * @deprecated Use ProviderCatalogContext. + */ +export type ProviderDiscoveryContext = ProviderCatalogContext; + +/** + * @deprecated Use ProviderCatalogResult. + */ +export type ProviderDiscoveryResult = ProviderCatalogResult; + +/** + * @deprecated Use ProviderPluginCatalog. + */ +export type ProviderPluginDiscovery = ProviderPluginCatalog; diff --git a/src/plugins/provider-plugin.types.ts b/src/plugins/provider-plugin.types.ts new file mode 100644 index 000000000000..b9b308c6eb7c --- /dev/null +++ b/src/plugins/provider-plugin.types.ts @@ -0,0 +1,693 @@ +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, + ProviderPluginDiscovery, +} from "./provider-catalog.types.js"; +import type { + ProviderApplyConfigDefaultsContext, + ProviderNormalizeConfigContext, + ProviderResolveConfigApiKeyContext, +} from "./provider-config-context.types.js"; +import type { + ProviderExternalAuthProfile, + ProviderExternalOAuthProfile, + ProviderResolveExternalAuthProfilesContext, + ProviderResolveExternalOAuthProfilesContext, + ProviderResolveSyntheticAuthContext, + ProviderSyntheticAuthResult, +} from "./provider-external-auth.types.js"; +import type { + ProviderReasoningOutputMode, + ProviderCapabilities, + 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, + ProviderThinkingPolicyContext, +} 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; + /** + * @deprecated Use catalog. + * + * Legacy alias for catalog. + * Kept for compatibility with existing provider plugins. + */ + discovery?: ProviderPluginDiscovery; + /** + * 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; + /** + * @deprecated Legacy static capability bag kept only for compatibility. + * + * New provider behavior should use explicit hooks instead. Core replay and + * stream/runtime logic no longer consumes this field. + */ + capabilities?: ProviderCapabilities; + /** + * 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 binary thinking toggle. + * + * Return true when the provider exposes a coarse on/off reasoning control + * instead of the normal multi-level ladder shown by `/think`. + * + * @deprecated Prefer `resolveThinkingProfile`. + */ + isBinaryThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | undefined; + /** + * Provider-owned xhigh reasoning support. + * + * Return true only for models that should expose the `xhigh` thinking level. + * + * @deprecated Prefer `resolveThinkingProfile`. + */ + supportsXHighThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | 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 default thinking level. + * + * Use this to keep model-family defaults (for example Claude 4.6 => + * adaptive) out of core command logic. + * + * @deprecated Prefer `resolveThinkingProfile`. + */ + resolveDefaultThinkingLevel?: ( + ctx: ProviderDefaultThinkingPolicyContext, + ) => "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive" | 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; + /** + * @deprecated Declare `contracts.externalAuthProviders` in the plugin manifest + * and implement `resolveExternalAuthProfiles` instead. Kept at the public + * plugin boundary until the SDK removal window closes. + */ + resolveExternalOAuthProfiles?: ( + ctx: ProviderResolveExternalOAuthProfilesContext, + ) => + | 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; +}; diff --git a/src/plugins/provider-replay.types.ts b/src/plugins/provider-replay.types.ts new file mode 100644 index 000000000000..0f0dd257bc84 --- /dev/null +++ b/src/plugins/provider-replay.types.ts @@ -0,0 +1,123 @@ +import type { AgentMessage } from "../agents/runtime/index.js"; +import type { AnyAgentTool } from "../agents/tools/common.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; + +export type ProviderReplaySanitizeMode = "full" | "images-only"; + +export type ProviderReplayToolCallIdMode = "strict" | "strict9"; + +export type ProviderReasoningOutputMode = "native" | "tagged"; + +/** + * @deprecated Legacy static provider capability bag. + * + * Core replay/runtime ownership now lives on explicit provider hooks such as + * `buildReplayPolicy`, `normalizeToolSchemas`, and `wrapStreamFn`. OpenClaw no + * longer reads this bag at runtime, but the field remains typed so existing + * third-party plugins do not fail to compile immediately. + */ +export type ProviderCapabilities = Record; + +/** + * Provider-owned replay/compaction transcript policy. + * + * These values are consumed by shared history replay and compaction logic. + * Return only the fields the provider wants to override; core fills the rest + * with its default policy. + */ +export type ProviderReplayPolicy = { + sanitizeMode?: ProviderReplaySanitizeMode; + sanitizeToolCallIds?: boolean; + toolCallIdMode?: ProviderReplayToolCallIdMode; + duplicateToolCallIdStyle?: "openai"; + preserveNativeAnthropicToolUseIds?: boolean; + preserveSignatures?: boolean; + sanitizeThoughtSignatures?: { + allowBase64Only?: boolean; + includeCamelCase?: boolean; + }; + dropThinkingBlocks?: boolean; + dropReasoningFromHistory?: boolean; + repairToolUseResultPairing?: boolean; + applyAssistantFirstOrderingFix?: boolean; + validateGeminiTurns?: boolean; + validateAnthropicTurns?: boolean; + allowSyntheticToolResults?: boolean; +}; + +/** + * Provider-owned replay/compaction policy input. + * + * Use this when transcript replay rules depend on provider/model transport + * behavior and should stay with the provider plugin instead of core tables. + */ +export type ProviderReplayPolicyContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env?: NodeJS.ProcessEnv; + provider: string; + modelId?: string; + modelApi?: string | null; + model?: ProviderRuntimeModel; +}; + +export type ProviderReplaySessionEntry = { + customType: string; + data?: unknown; +}; + +export type ProviderReplaySessionState = { + getCustomEntries(): ProviderReplaySessionEntry[]; + appendCustomEntry(customType: string, data: unknown): void; +}; + +/** + * Provider-owned replay-history sanitization input. + * + * Runs after core applies generic transcript cleanup so plugins can make + * provider-specific replay rewrites without owning the whole compaction flow. + */ +export type ProviderSanitizeReplayHistoryContext = ProviderReplayPolicyContext & { + sessionId: string; + messages: AgentMessage[]; + allowedToolNames?: Iterable; + sessionState?: ProviderReplaySessionState; +}; + +/** + * Provider-owned final replay-turn validation input. + * + * Use this for providers that require strict turn ordering or additional + * replay-time transcript validation beyond generic sanitation. + */ +export type ProviderValidateReplayTurnsContext = ProviderReplayPolicyContext & { + sessionId?: string; + messages: AgentMessage[]; + sessionState?: ProviderReplaySessionState; +}; + +/** + * Provider-owned tool-schema normalization input. + * + * Runs before tool registration for replay/compaction/inference so providers + * can rewrite schema keywords that their transport family does not support. + */ +export type ProviderNormalizeToolSchemasContext = ProviderReplayPolicyContext & { + tools: AnyAgentTool[]; +}; + +export type ProviderToolSchemaDiagnostic = { + toolName: string; + toolIndex?: number; + violations: string[]; +}; + +/** + * Provider-owned reasoning output mode input. + * + * Use this when a provider requires a specific reasoning-output contract, such + * as text tags instead of native structured reasoning fields. + */ +export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext; diff --git a/src/plugins/provider-runtime.types.ts b/src/plugins/provider-runtime.types.ts new file mode 100644 index 000000000000..d42bf139f26a --- /dev/null +++ b/src/plugins/provider-runtime.types.ts @@ -0,0 +1,303 @@ +import type { AuthProfileCredential, AuthProfileStore } from "../agents/auth-profiles/types.js"; +import type { ProviderSystemPromptContribution } from "../agents/system-prompt-contribution.js"; +import type { ReplyPayload } from "../auto-reply/reply-payload.js"; +import type { ThinkLevel } from "../auto-reply/thinking.shared.js"; +import type { ModelProviderConfig } from "../config/types.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { ModelRegistry } from "../llm/model-registry.js"; +import type { ProviderSystemPromptContributionContext } from "./provider-authentication.types.js"; +import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; + +type ModelProviderRequestTransportOverrides = + import("../agents/provider-request-config.js").ModelProviderRequestTransportOverrides; + +export type ProviderRuntimeProviderConfig = { + baseUrl?: string; + api?: ModelProviderConfig["api"]; + auth?: ModelProviderConfig["auth"]; + models?: ModelProviderConfig["models"]; + headers?: unknown; +}; + +/** + * Sync hook for provider-owned model ids that are not present in the local + * registry/catalog yet. + * + * Use this for pass-through providers or provider-specific forward-compat + * behavior. The hook should be cheap and side-effect free; async refreshes + * belong in `prepareDynamicModel`. + */ +export type ProviderResolveDynamicModelContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + agentRuntimeId?: string; + provider: string; + modelId: string; + modelRegistry: ModelRegistry; + providerConfig?: ProviderRuntimeProviderConfig; + authProfileId?: string; + authProfileMode?: AuthProfileCredential["type"] | "aws-sdk"; +}; + +/** + * Optional async warm-up for dynamic model resolution. + * + * Called only from async model resolution paths, before retrying + * `resolveDynamicModel`. This is the place to refresh caches or fetch provider + * metadata over the network. + */ +export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext; + +export type ProviderPreferRuntimeResolvedModelContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; +}; + +/** + * Last-chance rewrite hook for provider-owned transport normalization. + * + * Runs after OpenClaw resolves an explicit/discovered/dynamic model and before + * the embedded runner uses it. Typical uses: swap API ids, fix base URLs, or + * patch provider-specific compat bits. + */ +export type ProviderNormalizeResolvedModelContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; + model: ProviderRuntimeModel; +}; + +/** + * Provider-owned model-id normalization before config/runtime lookup. + * + * Use this for provider-specific alias cleanup that should stay with the + * plugin rather than in core string tables. + */ +export type ProviderNormalizeModelIdContext = { + provider: string; + modelId: string; +}; + +/** + * Provider-owned transport normalization for arbitrary provider/model config. + * + * Use this when transport cleanup depends on API/baseUrl rather than the + * owning provider id, for example custom providers that still target a + * plugin-owned transport family. + */ +export type ProviderNormalizeTransportContext = { + config?: OpenClawConfig; + workspaceDir?: string; + provider: string; + modelId?: string; + api?: string | null; + baseUrl?: string; +}; + +/** + * Runtime auth input for providers that need an extra exchange step before + * inference. The incoming `apiKey` is the raw credential resolved from auth + * profiles/env/config. The returned value should be the actual token/key to use + * for the request. + */ +export type ProviderPrepareRuntimeAuthContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + modelId: string; + model: ProviderRuntimeModel; + apiKey: string; + authMode: string; + profileId?: string; +}; + +/** + * Result of `prepareRuntimeAuth`. + * + * `apiKey` is required and becomes the runtime credential stored in auth + * storage. `baseUrl` is optional and lets providers like GitHub Copilot swap to + * an entitlement-specific endpoint at request time. `expiresAt` enables generic + * background refresh in long-running turns. + */ +export type ProviderPreparedRuntimeAuth = { + apiKey: string; + baseUrl?: string; + request?: ModelProviderRequestTransportOverrides; + expiresAt?: number; +}; + +/** + * Usage/billing auth input for providers that expose quota/usage endpoints. + * + * This hook is intentionally separate from `prepareRuntimeAuth`: usage + * snapshots often need a different credential source than live inference + * requests, and they run outside the embedded runner. + * + * The helper methods cover the common OpenClaw auth resolution paths: + * + * - `resolveApiKeyFromConfigAndStore`: env/config/plain token/api_key profiles + * - `resolveOAuthToken`: oauth/token profiles resolved through the auth store, + * optionally for an explicit provider override + * + * Plugins can still do extra provider-specific work on top (for example parse a + * token blob, read a legacy credential file, or pick between aliases). + */ +export type ProviderResolveUsageAuthContext = { + config: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + resolveApiKeyFromConfigAndStore: (params?: { + providerIds?: string[]; + envDirect?: Array; + }) => string | undefined; + /** Ordered API-key/token candidates, including resolved SecretRefs, for credential classification. */ + resolveApiKeyCandidatesFromConfigAndStore?: (params?: { + providerIds?: string[]; + envDirect?: Array; + }) => Promise; + resolveOAuthToken: (params?: { provider?: string }) => Promise; +}; + +export type ProviderUsageAuthToken = { + token: string; + accountId?: string; + /** Non-secret plan metadata from the resolved credential (e.g. Claude "max"). */ + subscriptionType?: string; + rateLimitTier?: string; + /** Account email captured on the resolved credential, when known. */ + email?: string; +}; + +/** + * Result of `resolveUsageAuth`. + * + * Two shapes are supported: + * - `{ token: string; accountId?: string }` — use this token for provider usage endpoints. + * - `{ handled: true }` — this provider handled the request but has no usable + * usage token; core must skip further fallback (generic API-key/OAuth fallback + * must not run). + * + * Returning `null` or `undefined` means "not handled by this provider"; core + * proceeds to generic fallback resolution. + */ +export type ProviderResolvedUsageAuth = ProviderUsageAuthToken | { handled: true }; + +/** + * Usage/quota snapshot input for providers that own their usage endpoint + * fetch/parsing behavior. + * + * This hook runs after `resolveUsageAuth` succeeds. Core still owns summary + * fan-out, timeout wrapping, filtering, and formatting; the provider plugin + * owns the provider-specific HTTP request + response normalization. + */ +export type ProviderFetchUsageSnapshotContext = { + config: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + token: string; + accountId?: string; + authProfileId?: string; + /** Non-secret plan metadata from the resolved credential (e.g. Claude "max"). */ + subscriptionType?: string; + rateLimitTier?: string; + /** Account email captured on the resolved credential, when known. */ + email?: string; + timeoutMs: number; + fetchFn: typeof fetch; +}; + +/** + * Provider-owned auth-doctor hint input. + * + * Called when OAuth refresh fails and OpenClaw wants a provider-specific repair + * hint to append to the generic re-auth message. Use this for legacy profile-id + * migrations or other provider-owned auth-store cleanup guidance. + */ +export type ProviderAuthDoctorHintContext = { + config?: OpenClawConfig; + store: AuthProfileStore; + provider: string; + profileId?: string; +}; + +/** + * Provider-owned extra-param normalization before OpenClaw builds its generic + * stream option wrapper. + * + * Use this to set provider defaults or rewrite provider-specific config keys + * into the merged `extraParams` object. Return the full next extraParams object. + */ +/** Provider-facing effort after OpenClaw lowers orchestration-only modes. */ +export type ProviderTransportThinkingLevel = Exclude; + +export type ProviderPrepareExtraParamsContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + agentId?: string; + nativeWebSearchAllowedByToolPolicy?: boolean; + provider: string; + modelId: string; + model?: ProviderRuntimeModel; + extraParams?: Record; + thinkingLevel?: ProviderTransportThinkingLevel; +}; + +export type ProviderExtraParamsForTransportContext = Omit< + ProviderPrepareExtraParamsContext, + "extraParams" +> & { + model?: ProviderRuntimeModel; + transport?: "sse" | "websocket" | "auto"; + extraParams: Record; +}; + +export type ProviderExtraParamsForTransportResult = { + patch?: Record | null; +}; + +export type ProviderResolvePromptOverlayContext = ProviderSystemPromptContributionContext & { + baseOverlay?: ProviderSystemPromptContribution; +}; + +export type ProviderFollowupFallbackRouteContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; + payload: ReplyPayload; + originatingChannel?: string; + originatingTo?: string; + originRoutable: boolean; + dispatcherAvailable: boolean; +}; + +export type ProviderFollowupFallbackRouteResult = { + route?: "origin" | "dispatcher" | "drop"; + reason?: string; +}; + +export type ProviderResolveAuthProfileIdContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; + preferredProfileId?: string; + lockedProfileId?: string; + profileOrder: string[]; + authStore: AuthProfileStore; +}; diff --git a/src/plugins/provider-transport.types.ts b/src/plugins/provider-transport.types.ts new file mode 100644 index 000000000000..0f567c2c8954 --- /dev/null +++ b/src/plugins/provider-transport.types.ts @@ -0,0 +1,184 @@ +import type { StreamFn } from "../agents/runtime/index.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; +import type { ProviderPrepareExtraParamsContext } from "./provider-runtime.types.js"; + +/** + * Provider-owned transport creation. + * + * Use this when the provider needs to replace shared model runtime's default transport with a + * custom StreamFn (for example a native API transport that cannot be expressed + * as a wrapper around `streamSimple`). + */ +export type ProviderCreateStreamFnContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + modelId: string; + model: ProviderRuntimeModel; +}; + +/** + * Provider-owned stream wrapper hook after OpenClaw applies its generic + * transport-independent wrappers. + * + * Use this for provider-specific payload/header/model mutations that still run + * through the normal `shared model runtime` stream path. + */ +export type ProviderWrapStreamFnContext = ProviderPrepareExtraParamsContext & { + model?: ProviderRuntimeModel; + streamFn?: StreamFn; +}; + +/** + * Provider-owned transport turn state. + * + * Use this for provider-native request headers or metadata that should stay + * stable across retries while still being attached by generic core transports. + */ +export type ProviderTransportTurnState = { + headers?: Record; + metadata?: Record; +}; + +/** + * Provider-owned request identity for transport turns. + * + * Use this when the provider exposes native request/session metadata that must + * be attached by both HTTP and WebSocket transports. + */ +export type ProviderResolveTransportTurnStateContext = { + provider: string; + modelId: string; + model?: ProviderRuntimeModel; + sessionId?: string; + turnId: string; + attempt: number; + transport: "stream" | "websocket"; +}; + +/** + * Provider-owned WebSocket session policy. + * + * Use this for session-scoped headers or cool-down behavior that should apply + * before a generic WebSocket transport decides to retry or fall back. + */ +export type ProviderWebSocketSessionPolicy = { + headers?: Record; + degradeCooldownMs?: number; +}; + +/** + * Provider-owned WebSocket session policy input. + * + * Use this when the provider wants to control native session handshake headers + * or the post-failure cool-down window for a generic WebSocket transport. + */ +export type ProviderResolveWebSocketSessionPolicyContext = { + provider: string; + modelId: string; + model?: ProviderRuntimeModel; + sessionId?: string; +}; + +/** + * Provider-owned failover error classification input. + * + * Use this when provider-specific transport or API errors need classification + * hints that generic string matching cannot express safely. + */ +export type ProviderFailoverErrorContext = { + provider?: string; + modelId?: string; + errorMessage: string; + status?: number; + code?: string; + errorType?: string; +}; + +/** + * Generic embedding provider shape returned by provider plugins. + * + * Keep this aligned with the memory embedding contract without forcing the + * plugin system to import memory internals directly. + */ +export type PluginEmbeddingProvider = { + id: string; + model: string; + maxInputTokens?: number; + embedQuery: (text: string, options?: { signal?: AbortSignal }) => Promise; + embedBatch: (texts: string[], options?: { signal?: AbortSignal }) => Promise; + embedBatchInputs?: (inputs: unknown[], options?: { signal?: AbortSignal }) => Promise; + client?: unknown; +}; + +/** + * Provider-owned embedding transport creation. + * + * Use this when a provider wants memory embeddings to live with the provider + * plugin instead of the core memory switchboard. + */ +export type ProviderCreateEmbeddingProviderContext = { + config: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + provider: string; + model: string; + remote?: { + baseUrl?: string; + apiKey?: unknown; + headers?: Record; + }; + providerApiKey?: string; + inputType?: string; + queryInputType?: string; + documentInputType?: string; + outputDimensionality?: number; + taskType?: string; +}; + +/** + * Provider-owned prompt-cache eligibility. + * + * Return `true` or `false` to override OpenClaw's built-in provider cache TTL + * detection for this provider. Return `undefined` to fall back to core rules. + */ +export type ProviderCacheTtlEligibilityContext = { + provider: string; + modelId: string; + modelApi?: string; +}; + +/** + * Provider-owned missing-auth message override. + * + * Runs only after OpenClaw exhausts normal env/profile/config auth resolution + * for the requested provider. Return a custom message to replace the generic + * "No API key found" error. + */ +export type ProviderBuildMissingAuthMessageContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + listProfileIds: (providerId: string) => string[]; +}; + +/** + * Provider-owned unknown-model hint override. + * + * Runs after catalog/runtime lookup misses for the requested provider. Return a + * hint suffix that OpenClaw should append to the generic `Unknown model` + * error. + */ +export type ProviderBuildUnknownModelHintContext = { + config?: OpenClawConfig; + agentDir?: string; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + provider: string; + modelId: string; + baseUrl?: string; +}; diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 02a32a3700a0..a54f72c64613 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,180 +1,11 @@ -import type { IncomingMessage, ServerResponse } from "node:http"; -import type { Duplex } from "node:stream"; -import type { - UnifiedModelCatalogEntry, - UnifiedModelCatalogKind, -} from "@openclaw/model-catalog-core/model-catalog-types"; -import type { Command } from "commander"; -import type { - ApiKeyCredential, - AuthProfileCredential, - OAuthCredential, - AuthProfileStore, -} from "../agents/auth-profiles/types.js"; -import type { FailoverReason } from "../agents/embedded-agent-helpers/types.js"; -import type { AgentHarness } from "../agents/harness/types.js"; -import type { ModelCatalogEntry } from "../agents/model-catalog.types.js"; -import type { AgentMessage } from "../agents/runtime/index.js"; -import type { StreamFn } from "../agents/runtime/index.js"; -import type { ProviderSystemPromptContribution } from "../agents/system-prompt-contribution.js"; -import type { PromptMode } from "../agents/system-prompt.types.js"; -import type { AnyAgentTool } from "../agents/tools/common.js"; -import type { ReplyPayload } from "../auto-reply/reply-payload.js"; -import type { ThinkLevel } from "../auto-reply/thinking.shared.js"; -import type { ModelProviderConfig } from "../config/types.js"; -import type { OpenClawConfig } from "../config/types.openclaw.js"; -import type { SecretRef } from "../config/types.secrets.js"; -import type { OperatorScope } from "../gateway/operator-scopes.js"; -import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; -import type { InternalHookHandler } from "../hooks/internal-hook-types.js"; -import type { ImageGenerationProvider } from "../image-generation/types.js"; -import type { - DiagnosticEventPrivateData, - DiagnosticEventInput, - DiagnosticEventMetadata, - DiagnosticEventPayload, -} from "../infra/diagnostic-events.js"; -import type { ProviderUsageSnapshot } from "../infra/provider-usage.types.js"; -import type { ModelRegistry } from "../llm/model-registry.js"; -import type { MediaUnderstandingProvider } from "../media-understanding/types.js"; -import type { MusicGenerationProvider } from "../music-generation/types.js"; -import type { - RealtimeTranscriptionProviderConfig, - RealtimeTranscriptionProviderConfiguredContext, - RealtimeTranscriptionProviderId, - RealtimeTranscriptionProviderResolveConfigContext, - RealtimeTranscriptionSession, - RealtimeTranscriptionSessionCreateRequest, -} from "../realtime-transcription/provider-types.js"; -import type { RuntimeEnv } from "../runtime.js"; -import type { SecurityAuditFinding } from "../security/audit.types.js"; -import type { JsonSchemaObject } from "../shared/json-schema.types.js"; -import type { - RealtimeVoiceBridge, - RealtimeVoiceBrowserSession, - RealtimeVoiceBrowserSessionCreateRequest, - RealtimeVoiceBridgeCreateRequest, - RealtimeVoiceProviderCapabilities, - RealtimeVoiceProviderConfig, - RealtimeVoiceProviderConfiguredContext, - RealtimeVoiceProviderId, - RealtimeVoiceProviderResolveConfigContext, -} from "../talk/provider-types.js"; -import type { TranscriptSourceProvider as TranscriptsSourceProviderCapability } from "../transcripts/provider-types.js"; -import type { - SpeechDirectiveTokenParseContext, - SpeechDirectiveTokenParseResult, - SpeechProviderConfiguredContext, - SpeechProviderConfig, - SpeechProviderResolveConfigContext, - SpeechProviderResolveTalkConfigContext, - SpeechProviderResolveTalkOverridesContext, - SpeechListVoicesRequest, - SpeechProviderPrepareSynthesisContext, - SpeechProviderPreparedSynthesis, - SpeechProviderId, - SpeechSynthesisRequest, - SpeechSynthesisResult, - SpeechSynthesisStreamRequest, - SpeechSynthesisStreamResult, - SpeechTelephonySynthesisRequest, - SpeechTelephonySynthesisResult, - SpeechVoiceOption, -} from "../tts/provider-types.js"; -import type { VideoGenerationProvider } from "../video-generation/types.js"; -import type { WizardPrompter } from "../wizard/prompts.js"; -import type { - AgentToolResultMiddleware, - AgentToolResultMiddlewareOptions, -} from "./agent-tool-result-middleware-types.js"; -import type { CliBackendPlugin, PluginTextTransforms } from "./cli-backend.types.js"; -import type { CodexAppServerExtensionFactory } from "./codex-app-server-extension-types.js"; -import type { - PluginConversationBinding, - PluginConversationBindingRequestParams, - PluginConversationBindingRequestResult, - PluginConversationBindingResolvedEvent, -} from "./conversation-binding.types.js"; -import type { PluginHookHandlerMap, PluginHookName } from "./hook-types.js"; -import type { - PluginAgentEventEmitParams, - PluginAgentEventEmitResult, - PluginAgentEventSubscriptionRegistration, - PluginControlUiDescriptor, - PluginJsonValue, - PluginNextTurnInjection, - PluginNextTurnInjectionEnqueueResult, - PluginRunContextGetParams, - PluginRunContextPatch, - PluginRuntimeLifecycleRegistration, - PluginSessionActionRegistration, - PluginSessionAttachmentParams, - PluginSessionAttachmentResult, - PluginSessionSchedulerJobHandle, - PluginSessionSchedulerJobRegistration, - PluginSessionExtensionRegistration, - PluginSessionTurnScheduleParams, - PluginSessionTurnUnscheduleByTagParams, - PluginSessionTurnUnscheduleByTagResult, - PluginToolMetadataRegistration, - PluginTrustedToolPolicyRegistration, -} from "./host-hooks.js"; -import type { PluginLogger } from "./logger-types.js"; -import type { PluginConfigUiHint } from "./manifest-types.js"; -import type { PluginKind } from "./plugin-kind.types.js"; -import type { SecretInputMode } from "./provider-auth-types.js"; -import type { - ProviderApplyConfigDefaultsContext, - ProviderNormalizeConfigContext, - ProviderResolveConfigApiKeyContext, -} from "./provider-config-context.types.js"; -import type { - ProviderAuthOptionBag, - ProviderExternalAuthProfile, - ProviderExternalOAuthProfile, - ProviderResolveExternalAuthProfilesContext, - ProviderResolveExternalOAuthProfilesContext, - ProviderResolveSyntheticAuthContext, - ProviderSyntheticAuthResult, -} from "./provider-external-auth.types.js"; -import type { createVpsAwareOAuthHandlers } from "./provider-oauth-flow.js"; -import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; -import type { - ProviderDefaultThinkingPolicyContext, - ProviderThinkingProfile, - ProviderThinkingPolicyContext, -} from "./provider-thinking.types.js"; -import type { PluginRuntime } from "./runtime/types.js"; -import type { SessionCatalogProvider } from "./session-catalog.js"; -import type { - OpenClawPluginHookOptions, - OpenClawPluginToolFactory, - OpenClawPluginToolOptions, -} from "./tool-types.js"; -import type { OpenClawPluginNodeHostCommand } from "./types.node-host.js"; -import type { WebFetchProviderPlugin, WebSearchProviderPlugin } from "./web-provider-types.js"; -type ModelProviderRequestTransportOverrides = - import("../agents/provider-request-config.js").ModelProviderRequestTransportOverrides; -type ChannelId = import("../channels/plugins/types.core.js").ChannelId; -type ChannelPlugin = import("../channels/plugins/types.plugin.js").ChannelPlugin; -export type { PluginRuntime } from "./runtime/types.js"; -export type { PluginOrigin } from "./plugin-origin.types.js"; -export type * from "./types.mcp-connection.js"; -export type { - PluginBundleFormat, - PluginConfigUiHint, - PluginDiagnostic, - PluginFormat, -} from "./manifest-types.js"; -export type { - OpenClawPluginActiveModelContext, - OpenClawPluginHookOptions, - OpenClawPluginToolContext, - OpenClawPluginToolFactory, - OpenClawPluginToolOptions, -} from "./tool-types.js"; -export type { AnyAgentTool } from "../agents/tools/common.js"; +/** + * Stable public facade for native plugin contracts. + * + * Domain types live in leaf modules so internal owners can depend on narrow + * surfaces without loading or navigating the complete plugin API contract. + */ export type { AgentHarness } from "../agents/harness/types.js"; +export type { AnyAgentTool } from "../agents/tools/common.js"; export type { AgentToolResultMiddleware, AgentToolResultMiddlewareContext, @@ -185,29 +16,29 @@ export type { AgentToolResultMiddlewareRuntime, OpenClawAgentToolResult, } from "./agent-tool-result-middleware-types.js"; -export type { - PluginConversationBinding, - PluginConversationBindingRequestParams, - PluginConversationBindingRequestResult, - PluginConversationBindingResolvedEvent, - PluginConversationBindingResolutionDecision, -} from "./conversation-binding.types.js"; export type { CliBackendAuthEpochMode, CliBackendExecutionMode, CliBackendNormalizeConfigContext, CliBackendNativeToolMode, + CliBackendPlugin, CliBackendPreparedExecution, CliBackendPrepareExecutionContext, CliBackendResolveExecutionArgs, CliBackendResolveExecutionArgsContext, CliBackendSideQuestionToolMode, CliBackendThinkingLevel, - CliBackendPlugin, CliBundleMcpMode, PluginTextReplacement, PluginTextTransforms, } from "./cli-backend.types.js"; +export type { + PluginConversationBinding, + PluginConversationBindingRequestParams, + PluginConversationBindingRequestResult, + PluginConversationBindingResolvedEvent, + PluginConversationBindingResolutionDecision, +} from "./conversation-binding.types.js"; export * from "./hook-types.js"; export type { PluginAgentEventEmitParams, @@ -225,15 +56,15 @@ export type { PluginRunContextGetParams, PluginRunContextPatch, PluginRuntimeLifecycleRegistration, - PluginSessionAttachmentParams, - PluginSessionAttachmentResult, - PluginSessionSchedulerJobHandle, - PluginSessionSchedulerJobRegistration, - PluginSessionExtensionRegistration, - PluginSessionExtensionProjection, PluginSessionActionContext, PluginSessionActionRegistration, PluginSessionActionResult, + PluginSessionAttachmentParams, + PluginSessionAttachmentResult, + PluginSessionExtensionProjection, + PluginSessionExtensionRegistration, + PluginSessionSchedulerJobHandle, + PluginSessionSchedulerJobRegistration, PluginSessionTurnScheduleParams, PluginSessionTurnUnscheduleByTagParams, PluginSessionTurnUnscheduleByTagResult, @@ -241,16 +72,47 @@ export type { PluginTrustedToolPolicyRegistration, } from "./host-hooks.js"; export type { PluginLogger } from "./logger-types.js"; -export type { PluginKind } from "./plugin-kind.types.js"; export type { + PluginBundleFormat, + PluginConfigUiHint, + PluginDiagnostic, + PluginFormat, +} from "./manifest-types.js"; +export type { PluginKind } from "./plugin-kind.types.js"; +export type { PluginOrigin } from "./plugin-origin.types.js"; +export type { + ProviderApplyConfigDefaultsContext, + ProviderNormalizeConfigContext, + ProviderResolveConfigApiKeyContext, +} from "./provider-config-context.types.js"; +export type { + ProviderAuthOptionBag, ProviderExternalAuthProfile, ProviderExternalOAuthProfile, - ProviderAuthOptionBag, ProviderResolveExternalAuthProfilesContext, ProviderResolveExternalOAuthProfilesContext, ProviderResolveSyntheticAuthContext, ProviderSyntheticAuthResult, } from "./provider-external-auth.types.js"; +export type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; +export type { + ProviderDefaultThinkingPolicyContext, + ProviderThinkingPolicyContext, + ProviderThinkingProfile, +} from "./provider-thinking.types.js"; +export type { PluginRuntime } from "./runtime/types.js"; +export type { + OpenClawPluginActiveModelContext, + OpenClawPluginHookOptions, + OpenClawPluginToolContext, + OpenClawPluginToolFactory, + OpenClawPluginToolOptions, +} from "./tool-types.js"; +export type { + OpenClawPluginNodeHostCommand, + OpenClawPluginNodeHostCommandAvailabilityContext, + OpenClawPluginNodeHostCommandIo, +} from "./types.node-host.js"; export type { PluginWebFetchProviderEntry, PluginWebSearchProviderEntry, @@ -269,2783 +131,20 @@ export type { WebSearchProviderToolExecutionContext, WebSearchRuntimeMetadataContext, } from "./web-provider-types.js"; -export type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; - -export type PluginConfigValidation = - | { ok: true; value?: unknown } - | { ok: false; errors: string[] }; - -/** - * Config schema contract accepted by plugin manifests and runtime registration. - * - * Plugins can provide a Zod-like parser, a lightweight `validate(...)` - * function, or both. `uiHints` and `jsonSchema` are optional extras for docs, - * forms, and config UIs. - */ -export type OpenClawPluginConfigSchema = { - safeParse?: (value: unknown) => { - success: boolean; - data?: unknown; - error?: { - issues?: Array<{ path: Array; message: string }>; - }; - }; - parse?: (value: unknown) => unknown; - validate?: (value: unknown) => PluginConfigValidation; - uiHints?: Record; - jsonSchema?: JsonSchemaObject; -}; - -export type ProviderAuthKind = "oauth" | "api_key" | "token" | "device_code" | "custom"; - -/** Standard result payload returned by provider auth methods. */ -export type ProviderAuthResult = { - profiles: Array<{ profileId: string; credential: AuthProfileCredential }>; - /** - * Optional config patch to merge after credentials are written. - * - * Use this for provider-owned onboarding defaults such as - * `models.providers.` entries, default aliases, or agent model helpers. - * The caller still persists auth-profile bindings separately. - */ - configPatch?: Partial; - defaultModel?: string; - notes?: string[]; - /** - * Opt in to replace `agents.defaults.models` wholesale with the patch map. - * Default behavior merges the map so other providers' entries survive. - * Set only from migrations that intentionally rename/remove model keys. - */ - replaceDefaultModels?: boolean; -}; - -/** Interactive auth context passed to provider login/setup methods. */ -export type ProviderAuthContext = { - config: OpenClawConfig; - env?: NodeJS.ProcessEnv; - agentDir?: string; - workspaceDir?: string; - prompter: WizardPrompter; - runtime: RuntimeEnv; - /** Cancels browser callbacks, device polling, and other app-owned auth work. */ - signal?: AbortSignal; - /** - * Optional onboarding CLI options that triggered this auth flow. - * - * Present for setup/configure/auth-choice flows so provider methods can - * honor preseeded flags like `--openai-api-key` or generic - * `--token/--token-provider` pairs. Direct `models auth login` usually - * leaves this undefined. - */ - opts?: ProviderAuthOptionBag; - /** - * Onboarding secret persistence preference. - * - * Interactive wizard flows set this when the caller explicitly requested - * plaintext or env/file/exec ref storage. Ad-hoc `models auth login` flows - * usually leave it undefined. - */ - secretInputMode?: SecretInputMode; - /** - * Whether the provider auth flow should offer the onboarding secret-storage - * mode picker when `secretInputMode` is unset. - * - * This is true for onboarding/configure flows and false for direct - * `models auth` commands, which should keep a tighter, provider-owned prompt - * surface. - */ - allowSecretRefPrompt?: boolean; - isRemote: boolean; - openUrl: (url: string) => Promise; - oauth: { - createVpsAwareHandlers: typeof createVpsAwareOAuthHandlers; - }; -}; - -export type ProviderNonInteractiveApiKeyResult = { - key: string; - source: "profile" | "env" | "flag"; - envVarName?: string; -}; - -export type ProviderResolveNonInteractiveApiKeyParams = { - provider: string; - flagValue?: string; - flagName: `--${string}`; - envVar: string; - envVarName?: string; - allowProfile?: boolean; - required?: boolean; -}; - -export type ProviderNonInteractiveApiKeyCredentialParams = { - provider: string; - resolved: ProviderNonInteractiveApiKeyResult; - email?: string; - metadata?: Record; -}; - -export type ProviderAuthMethodNonInteractiveContext = { - authChoice: string; - config: OpenClawConfig; - baseConfig: OpenClawConfig; - opts: ProviderAuthOptionBag; - runtime: RuntimeEnv; - agentDir?: string; - workspaceDir?: string; - resolveApiKey: ( - params: ProviderResolveNonInteractiveApiKeyParams, - ) => Promise; - toApiKeyCredential: ( - params: ProviderNonInteractiveApiKeyCredentialParams, - ) => ApiKeyCredential | null; -}; - -export type ProviderAuthMethod = { - id: string; - label: string; - hint?: string; - kind: ProviderAuthKind; - /** Provider-owned model used to validate app-guided secret setup. */ - starterModel?: string; - /** - * Optional wizard/onboarding metadata for this specific auth method. - * - * Use this when one provider exposes multiple setup entries (for example API - * key + OAuth, or region-specific login flows). OpenClaw uses this to expose - * method-specific auth choices while keeping the provider id stable. - */ - wizard?: ProviderPluginWizardSetup; - run: (ctx: ProviderAuthContext) => Promise; - runNonInteractive?: ( - ctx: ProviderAuthMethodNonInteractiveContext, - ) => Promise; -}; - -export type ProviderCatalogOrder = "simple" | "profile" | "paired" | "late"; - -export type ProviderCatalogContext = { - config: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - resolveProviderApiKey: (providerId?: string) => { - apiKey: string | undefined; - discoveryApiKey?: string; - }; - resolveProviderAuth: ( - providerId?: string, - options?: { - oauthMarker?: string; - }, - ) => { - apiKey: string | undefined; - discoveryApiKey?: string; - mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none"; - source: "env" | "profile" | "none"; - profileId?: string; - }; -}; - -export type ProviderCatalogResult = - | { provider: ModelProviderConfig } - | { providers: Record } - | null - | undefined; - -export type ProviderPluginCatalog = { - order?: ProviderCatalogOrder; - run: (ctx: ProviderCatalogContext) => Promise; -}; - -export type UnifiedModelCatalogProviderContext = ProviderCatalogContext & { - signal?: AbortSignal; - includeLive?: boolean; - timeoutMs?: number; -}; - -export type UnifiedModelCatalogProviderPlugin = { - provider: string; - kinds: readonly UnifiedModelCatalogKind[]; - staticCatalog?: ( - ctx: UnifiedModelCatalogProviderContext, - ) => - | readonly UnifiedModelCatalogEntry[] - | Promise - | null - | undefined; - liveCatalog?: ( - ctx: UnifiedModelCatalogProviderContext, - ) => - | readonly UnifiedModelCatalogEntry[] - | Promise - | null - | undefined; -}; - -export type ProviderRuntimeProviderConfig = { - baseUrl?: string; - api?: ModelProviderConfig["api"]; - auth?: ModelProviderConfig["auth"]; - models?: ModelProviderConfig["models"]; - headers?: unknown; -}; - -/** - * Sync hook for provider-owned model ids that are not present in the local - * registry/catalog yet. - * - * Use this for pass-through providers or provider-specific forward-compat - * behavior. The hook should be cheap and side-effect free; async refreshes - * belong in `prepareDynamicModel`. - */ -export type ProviderResolveDynamicModelContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - agentRuntimeId?: string; - provider: string; - modelId: string; - modelRegistry: ModelRegistry; - providerConfig?: ProviderRuntimeProviderConfig; - authProfileId?: string; - authProfileMode?: AuthProfileCredential["type"] | "aws-sdk"; -}; - -/** - * Optional async warm-up for dynamic model resolution. - * - * Called only from async model resolution paths, before retrying - * `resolveDynamicModel`. This is the place to refresh caches or fetch provider - * metadata over the network. - */ -export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext; - -export type ProviderPreferRuntimeResolvedModelContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; -}; - -/** - * Last-chance rewrite hook for provider-owned transport normalization. - * - * Runs after OpenClaw resolves an explicit/discovered/dynamic model and before - * the embedded runner uses it. Typical uses: swap API ids, fix base URLs, or - * patch provider-specific compat bits. - */ -export type ProviderNormalizeResolvedModelContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; - model: ProviderRuntimeModel; -}; - -/** - * Provider-owned model-id normalization before config/runtime lookup. - * - * Use this for provider-specific alias cleanup that should stay with the - * plugin rather than in core string tables. - */ -export type ProviderNormalizeModelIdContext = { - provider: string; - modelId: string; -}; - -export type { - ProviderApplyConfigDefaultsContext, - ProviderNormalizeConfigContext, - ProviderResolveConfigApiKeyContext, -} from "./provider-config-context.types.js"; - -/** - * Provider-owned transport normalization for arbitrary provider/model config. - * - * Use this when transport cleanup depends on API/baseUrl rather than the - * owning provider id, for example custom providers that still target a - * plugin-owned transport family. - */ -export type ProviderNormalizeTransportContext = { - config?: OpenClawConfig; - workspaceDir?: string; - provider: string; - modelId?: string; - api?: string | null; - baseUrl?: string; -}; - -/** - * Runtime auth input for providers that need an extra exchange step before - * inference. The incoming `apiKey` is the raw credential resolved from auth - * profiles/env/config. The returned value should be the actual token/key to use - * for the request. - */ -export type ProviderPrepareRuntimeAuthContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - modelId: string; - model: ProviderRuntimeModel; - apiKey: string; - authMode: string; - profileId?: string; -}; - -/** - * Result of `prepareRuntimeAuth`. - * - * `apiKey` is required and becomes the runtime credential stored in auth - * storage. `baseUrl` is optional and lets providers like GitHub Copilot swap to - * an entitlement-specific endpoint at request time. `expiresAt` enables generic - * background refresh in long-running turns. - */ -export type ProviderPreparedRuntimeAuth = { - apiKey: string; - baseUrl?: string; - request?: ModelProviderRequestTransportOverrides; - expiresAt?: number; -}; - -/** - * Usage/billing auth input for providers that expose quota/usage endpoints. - * - * This hook is intentionally separate from `prepareRuntimeAuth`: usage - * snapshots often need a different credential source than live inference - * requests, and they run outside the embedded runner. - * - * The helper methods cover the common OpenClaw auth resolution paths: - * - * - `resolveApiKeyFromConfigAndStore`: env/config/plain token/api_key profiles - * - `resolveOAuthToken`: oauth/token profiles resolved through the auth store, - * optionally for an explicit provider override - * - * Plugins can still do extra provider-specific work on top (for example parse a - * token blob, read a legacy credential file, or pick between aliases). - */ -export type ProviderResolveUsageAuthContext = { - config: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - resolveApiKeyFromConfigAndStore: (params?: { - providerIds?: string[]; - envDirect?: Array; - }) => string | undefined; - /** Ordered API-key/token candidates, including resolved SecretRefs, for credential classification. */ - resolveApiKeyCandidatesFromConfigAndStore?: (params?: { - providerIds?: string[]; - envDirect?: Array; - }) => Promise; - resolveOAuthToken: (params?: { provider?: string }) => Promise; -}; - -export type ProviderUsageAuthToken = { - token: string; - accountId?: string; - /** Non-secret plan metadata from the resolved credential (e.g. Claude "max"). */ - subscriptionType?: string; - rateLimitTier?: string; - /** Account email captured on the resolved credential, when known. */ - email?: string; -}; - -/** - * Result of `resolveUsageAuth`. - * - * Two shapes are supported: - * - `{ token: string; accountId?: string }` — use this token for provider usage endpoints. - * - `{ handled: true }` — this provider handled the request but has no usable - * usage token; core must skip further fallback (generic API-key/OAuth fallback - * must not run). - * - * Returning `null` or `undefined` means "not handled by this provider"; core - * proceeds to generic fallback resolution. - */ -export type ProviderResolvedUsageAuth = ProviderUsageAuthToken | { handled: true }; - -/** - * Usage/quota snapshot input for providers that own their usage endpoint - * fetch/parsing behavior. - * - * This hook runs after `resolveUsageAuth` succeeds. Core still owns summary - * fan-out, timeout wrapping, filtering, and formatting; the provider plugin - * owns the provider-specific HTTP request + response normalization. - */ -export type ProviderFetchUsageSnapshotContext = { - config: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - token: string; - accountId?: string; - authProfileId?: string; - /** Non-secret plan metadata from the resolved credential (e.g. Claude "max"). */ - subscriptionType?: string; - rateLimitTier?: string; - /** Account email captured on the resolved credential, when known. */ - email?: string; - timeoutMs: number; - fetchFn: typeof fetch; -}; - -/** - * Provider-owned auth-doctor hint input. - * - * Called when OAuth refresh fails and OpenClaw wants a provider-specific repair - * hint to append to the generic re-auth message. Use this for legacy profile-id - * migrations or other provider-owned auth-store cleanup guidance. - */ -export type ProviderAuthDoctorHintContext = { - config?: OpenClawConfig; - store: AuthProfileStore; - provider: string; - profileId?: string; -}; - -/** - * Provider-owned extra-param normalization before OpenClaw builds its generic - * stream option wrapper. - * - * Use this to set provider defaults or rewrite provider-specific config keys - * into the merged `extraParams` object. Return the full next extraParams object. - */ -/** Provider-facing effort after OpenClaw lowers orchestration-only modes. */ -export type ProviderTransportThinkingLevel = Exclude; - -export type ProviderPrepareExtraParamsContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - agentId?: string; - nativeWebSearchAllowedByToolPolicy?: boolean; - provider: string; - modelId: string; - model?: ProviderRuntimeModel; - extraParams?: Record; - thinkingLevel?: ProviderTransportThinkingLevel; -}; - -export type ProviderExtraParamsForTransportContext = Omit< - ProviderPrepareExtraParamsContext, - "extraParams" -> & { - model?: ProviderRuntimeModel; - transport?: "sse" | "websocket" | "auto"; - extraParams: Record; -}; - -export type ProviderExtraParamsForTransportResult = { - patch?: Record | null; -}; - -export type ProviderResolvePromptOverlayContext = ProviderSystemPromptContributionContext & { - baseOverlay?: ProviderSystemPromptContribution; -}; - -export type ProviderFollowupFallbackRouteContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; - payload: ReplyPayload; - originatingChannel?: string; - originatingTo?: string; - originRoutable: boolean; - dispatcherAvailable: boolean; -}; - -export type ProviderFollowupFallbackRouteResult = { - route?: "origin" | "dispatcher" | "drop"; - reason?: string; -}; - -export type ProviderResolveAuthProfileIdContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; - preferredProfileId?: string; - lockedProfileId?: string; - profileOrder: string[]; - authStore: AuthProfileStore; -}; - -export type ProviderReplaySanitizeMode = "full" | "images-only"; - -export type ProviderReplayToolCallIdMode = "strict" | "strict9"; - -export type ProviderReasoningOutputMode = "native" | "tagged"; - -/** - * @deprecated Legacy static provider capability bag. - * - * Core replay/runtime ownership now lives on explicit provider hooks such as - * `buildReplayPolicy`, `normalizeToolSchemas`, and `wrapStreamFn`. OpenClaw no - * longer reads this bag at runtime, but the field remains typed so existing - * third-party plugins do not fail to compile immediately. - */ -export type ProviderCapabilities = Record; - -/** - * Provider-owned replay/compaction transcript policy. - * - * These values are consumed by shared history replay and compaction logic. - * Return only the fields the provider wants to override; core fills the rest - * with its default policy. - */ -export type ProviderReplayPolicy = { - sanitizeMode?: ProviderReplaySanitizeMode; - sanitizeToolCallIds?: boolean; - toolCallIdMode?: ProviderReplayToolCallIdMode; - duplicateToolCallIdStyle?: "openai"; - preserveNativeAnthropicToolUseIds?: boolean; - preserveSignatures?: boolean; - sanitizeThoughtSignatures?: { - allowBase64Only?: boolean; - includeCamelCase?: boolean; - }; - dropThinkingBlocks?: boolean; - dropReasoningFromHistory?: boolean; - repairToolUseResultPairing?: boolean; - applyAssistantFirstOrderingFix?: boolean; - validateGeminiTurns?: boolean; - validateAnthropicTurns?: boolean; - allowSyntheticToolResults?: boolean; -}; - -/** - * Provider-owned replay/compaction policy input. - * - * Use this when transcript replay rules depend on provider/model transport - * behavior and should stay with the provider plugin instead of core tables. - */ -export type ProviderReplayPolicyContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env?: NodeJS.ProcessEnv; - provider: string; - modelId?: string; - modelApi?: string | null; - model?: ProviderRuntimeModel; -}; - -export type ProviderReplaySessionEntry = { - customType: string; - data?: unknown; -}; - -export type ProviderReplaySessionState = { - getCustomEntries(): ProviderReplaySessionEntry[]; - appendCustomEntry(customType: string, data: unknown): void; -}; - -/** - * Provider-owned replay-history sanitization input. - * - * Runs after core applies generic transcript cleanup so plugins can make - * provider-specific replay rewrites without owning the whole compaction flow. - */ -export type ProviderSanitizeReplayHistoryContext = ProviderReplayPolicyContext & { - sessionId: string; - messages: AgentMessage[]; - allowedToolNames?: Iterable; - sessionState?: ProviderReplaySessionState; -}; - -/** - * Provider-owned final replay-turn validation input. - * - * Use this for providers that require strict turn ordering or additional - * replay-time transcript validation beyond generic sanitation. - */ -export type ProviderValidateReplayTurnsContext = ProviderReplayPolicyContext & { - sessionId?: string; - messages: AgentMessage[]; - sessionState?: ProviderReplaySessionState; -}; - -/** - * Provider-owned tool-schema normalization input. - * - * Runs before tool registration for replay/compaction/inference so providers - * can rewrite schema keywords that their transport family does not support. - */ -export type ProviderNormalizeToolSchemasContext = ProviderReplayPolicyContext & { - tools: AnyAgentTool[]; -}; - -export type ProviderToolSchemaDiagnostic = { - toolName: string; - toolIndex?: number; - violations: string[]; -}; - -/** - * Provider-owned reasoning output mode input. - * - * Use this when a provider requires a specific reasoning-output contract, such - * as text tags instead of native structured reasoning fields. - */ -export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext; - -/** - * Provider-owned transport creation. - * - * Use this when the provider needs to replace shared model runtime's default transport with a - * custom StreamFn (for example a native API transport that cannot be expressed - * as a wrapper around `streamSimple`). - */ -export type ProviderCreateStreamFnContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; - model: ProviderRuntimeModel; -}; - -/** - * Provider-owned stream wrapper hook after OpenClaw applies its generic - * transport-independent wrappers. - * - * Use this for provider-specific payload/header/model mutations that still run - * through the normal `shared model runtime` stream path. - */ -export type ProviderWrapStreamFnContext = ProviderPrepareExtraParamsContext & { - model?: ProviderRuntimeModel; - streamFn?: StreamFn; -}; - -/** - * Provider-owned transport turn state. - * - * Use this for provider-native request headers or metadata that should stay - * stable across retries while still being attached by generic core transports. - */ -export type ProviderTransportTurnState = { - headers?: Record; - metadata?: Record; -}; - -/** - * Provider-owned request identity for transport turns. - * - * Use this when the provider exposes native request/session metadata that must - * be attached by both HTTP and WebSocket transports. - */ -export type ProviderResolveTransportTurnStateContext = { - provider: string; - modelId: string; - model?: ProviderRuntimeModel; - sessionId?: string; - turnId: string; - attempt: number; - transport: "stream" | "websocket"; -}; - -/** - * Provider-owned WebSocket session policy. - * - * Use this for session-scoped headers or cool-down behavior that should apply - * before a generic WebSocket transport decides to retry or fall back. - */ -export type ProviderWebSocketSessionPolicy = { - headers?: Record; - degradeCooldownMs?: number; -}; - -/** - * Provider-owned WebSocket session policy input. - * - * Use this when the provider wants to control native session handshake headers - * or the post-failure cool-down window for a generic WebSocket transport. - */ -export type ProviderResolveWebSocketSessionPolicyContext = { - provider: string; - modelId: string; - model?: ProviderRuntimeModel; - sessionId?: string; -}; - -/** - * Provider-owned failover error classification input. - * - * Use this when provider-specific transport or API errors need classification - * hints that generic string matching cannot express safely. - */ -export type ProviderFailoverErrorContext = { - provider?: string; - modelId?: string; - errorMessage: string; - status?: number; - code?: string; - errorType?: string; -}; - -/** - * Generic embedding provider shape returned by provider plugins. - * - * Keep this aligned with the memory embedding contract without forcing the - * plugin system to import memory internals directly. - */ -export type PluginEmbeddingProvider = { - id: string; - model: string; - maxInputTokens?: number; - embedQuery: (text: string, options?: { signal?: AbortSignal }) => Promise; - embedBatch: (texts: string[], options?: { signal?: AbortSignal }) => Promise; - embedBatchInputs?: (inputs: unknown[], options?: { signal?: AbortSignal }) => Promise; - client?: unknown; -}; - -/** - * Provider-owned embedding transport creation. - * - * Use this when a provider wants memory embeddings to live with the provider - * plugin instead of the core memory switchboard. - */ -export type ProviderCreateEmbeddingProviderContext = { - config: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - model: string; - remote?: { - baseUrl?: string; - apiKey?: unknown; - headers?: Record; - }; - providerApiKey?: string; - inputType?: string; - queryInputType?: string; - documentInputType?: string; - outputDimensionality?: number; - taskType?: string; -}; - -/** - * Provider-owned prompt-cache eligibility. - * - * Return `true` or `false` to override OpenClaw's built-in provider cache TTL - * detection for this provider. Return `undefined` to fall back to core rules. - */ -export type ProviderCacheTtlEligibilityContext = { - provider: string; - modelId: string; - modelApi?: string; -}; - -/** - * Provider-owned missing-auth message override. - * - * Runs only after OpenClaw exhausts normal env/profile/config auth resolution - * for the requested provider. Return a custom message to replace the generic - * "No API key found" error. - */ -export type ProviderBuildMissingAuthMessageContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - listProfileIds: (providerId: string) => string[]; -}; - -/** - * Provider-owned unknown-model hint override. - * - * Runs after catalog/runtime lookup misses for the requested provider. Return a - * hint suffix that OpenClaw should append to the generic `Unknown model` - * error. - */ -export type ProviderBuildUnknownModelHintContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - modelId: string; - baseUrl?: string; -}; - -/** - * Built-in model suppression hook context. - * - * @deprecated Use manifest `modelCatalog.suppressions`. Runtime suppression - * hooks are no longer called by model resolution. - */ -export type ProviderBuiltInModelSuppressionContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - provider: string; - modelId: string; - baseUrl?: string; -}; - -export type ProviderBuiltInModelSuppressionResult = { - suppress: boolean; - errorMessage?: string; -}; - -export type { - ProviderDefaultThinkingPolicyContext, - ProviderThinkingProfile, - ProviderThinkingPolicyContext, -} from "./provider-thinking.types.js"; - -/** - * Provider-owned "modern model" policy input. - * - * Live smoke/model-profile selection uses this to keep provider-specific - * inclusion/exclusion rules out of core. - */ -export type ProviderModernModelPolicyContext = { - provider: string; - modelId: string; -}; - -/** - * Final catalog augmentation hook. - * - * Runs after OpenClaw loads the discovered model catalog and merges configured - * opt-in providers. Use this for forward-compat rows or vendor-owned synthetic - * entries that should appear in `models list` and model pickers even when the - * upstream registry has not caught up yet. - */ -export type ProviderAugmentModelCatalogContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - resolveProviderApiKey?: ProviderCatalogContext["resolveProviderApiKey"]; - entries: ModelCatalogEntry[]; -}; - -/** - * @deprecated Use ProviderCatalogOrder. - */ -export type ProviderDiscoveryOrder = ProviderCatalogOrder; - -/** - * @deprecated Use ProviderCatalogContext. - */ -export type ProviderDiscoveryContext = ProviderCatalogContext; - -/** - * @deprecated Use ProviderCatalogResult. - */ -export type ProviderDiscoveryResult = ProviderCatalogResult; - -/** - * @deprecated Use ProviderPluginCatalog. - */ -export type ProviderPluginDiscovery = ProviderPluginCatalog; - -export type ProviderPluginWizardSetup = { - choiceId?: string; - choiceLabel?: string; - choiceHint?: string; - assistantPriority?: number; - assistantVisibility?: "visible" | "manual-only"; - onboardingFeatured?: boolean; - groupId?: string; - groupLabel?: string; - groupHint?: string; - methodId?: string; - /** - * Interactive onboarding surfaces where this auth choice should appear. - * Defaults to `["text-inference"]` when omitted. - */ - onboardingScopes?: Array<"text-inference" | "image-generation" | "music-generation">; - /** - * Optional model-allowlist prompt policy applied after this auth choice is - * selected in configure/onboarding flows. - * - * Keep this UI-facing and static. Provider logic that needs runtime state - * should stay in `run`/`runNonInteractive`. - */ - modelAllowlist?: { - allowedKeys?: string[]; - initialSelections?: string[]; - loadCatalog?: boolean; - message?: string; - }; - /** - * Optional default-model prompt policy for this auth/setup choice. - * - * Use this when selecting the auth choice should still force a model picker - * even if the choice was preseeded via CLI/configure, or when "keep current" - * would skip required provider-owned post-selection work. - */ - modelSelection?: { - promptWhenAuthChoiceProvided?: boolean; - allowKeepCurrent?: boolean; - }; -}; - -/** Optional model-picker metadata shown in interactive provider selection flows. */ -export type ProviderPluginWizardModelPicker = { - label?: string; - hint?: string; - methodId?: string; -}; - -/** UI metadata that lets provider plugins appear in onboarding and configure flows. */ -export type ProviderPluginWizard = { - setup?: ProviderPluginWizardSetup; - modelPicker?: ProviderPluginWizardModelPicker; -}; - -export type ProviderOAuthProfileIdRepair = { - /** - * Legacy OAuth profile id to migrate away from. - * - * When omitted, OpenClaw falls back to `:default`. - */ - legacyProfileId?: string; - /** - * Optional custom doctor prompt label. - * - * Defaults to the provider label when omitted. - */ - promptLabel?: string; -}; - -export type ProviderModelSelectedContext = { - config: OpenClawConfig; - model: string; - prompter: WizardPrompter; - agentDir?: string; - workspaceDir?: string; -}; - -export type ProviderDeferSyntheticProfileAuthContext = { - config?: OpenClawConfig; - provider: string; - providerConfig?: ModelProviderConfig; - resolvedApiKey?: string; -}; - -export type ProviderSystemPromptContributionContext = { - config?: OpenClawConfig; - agentDir?: string; - workspaceDir?: string; - provider: string; - modelId: string; - promptMode: PromptMode; - runtimeChannel?: string; - runtimeCapabilities?: string[]; - agentId?: string; - trigger?: "cron" | "heartbeat" | "manual" | "memory" | "overflow" | "user"; -}; - -export type ProviderTransformSystemPromptContext = ProviderSystemPromptContributionContext & { - systemPrompt: string; -}; - -export type PluginTextTransformRegistration = PluginTextTransforms; - -/** JSON-compatible provider settings for one configured worker profile. */ -export type WorkerProfile = Readonly>; - -/** SSH endpoint material returned by a worker provider after provisioning. */ -export type WorkerSshEndpoint = { - host: string; - port: number; - user: string; - /** OpenSSH public host-key line obtained from trusted provisioning output. */ - hostKey: string; - /** Secret reference only; providers must never return plaintext key material. */ - keyRef: SecretRef; -}; - -/** Resolved SSH client identity. Providers may return a local path or ephemeral material. */ -export type WorkerSshIdentity = - | { kind: "path"; path: string } - | { kind: "material"; contents: string }; - -/** Durable context supplied when a worker provider resolves the identity it minted. */ -export type WorkerSshIdentityRequest = { - leaseId: string; - profile: WorkerProfile; - keyRef: SecretRef; -}; - -/** Durable lease identity and endpoint returned by a successful provision operation. */ -export type WorkerLease = { - leaseId: string; - ssh: WorkerSshEndpoint; -}; - -/** Authoritative inspection result for an already-known worker lease. */ -export type WorkerLeaseStatus = - | { status: "active" } - | { status: "destroyed" } - | { status: "unknown" }; - -/** Permanent provider rejection recorded as a terminal worker failure. */ -export class WorkerProviderError extends Error { - readonly code = "invalid_profile"; - - constructor(message: string) { - super(message); - this.name = "WorkerProviderError"; - } -} - -/** Cloud-worker lifecycle capability registered by a plugin. */ -export type WorkerProvider = { - id: string; - /** - * Provision or adopt the lease for this operation id. - * Repeating the same operation id must be idempotent across gateway restarts. - */ - provision: (profile: WorkerProfile, operationId: string) => Promise; - /** Throws on transient/indeterminate failures; `unknown` means authoritative absence. */ - inspect: (lease: { leaseId: string; profile: WorkerProfile }) => Promise; - /** - * Resolves provider-owned dynamic identities. When absent, the gateway uses its generic - * SecretRef resolver; when present, failures are authoritative and never fall back. - */ - resolveSshIdentity?: (request: WorkerSshIdentityRequest) => Promise; - renew?: (leaseId: string) => Promise; - /** Idempotent; resolves only after the provider can prove teardown. */ - destroy: (lease: { leaseId: string; profile: WorkerProfile }) => Promise; -}; - -/** Text-inference provider capability registered by a plugin. */ -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; - /** - * @deprecated Use catalog. - * - * Legacy alias for catalog. - * Kept for compatibility with existing provider plugins. - */ - discovery?: ProviderPluginDiscovery; - /** - * 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; - /** - * @deprecated Legacy static capability bag kept only for compatibility. - * - * New provider behavior should use explicit hooks instead. Core replay and - * stream/runtime logic no longer consumes this field. - */ - capabilities?: ProviderCapabilities; - /** - * 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 binary thinking toggle. - * - * Return true when the provider exposes a coarse on/off reasoning control - * instead of the normal multi-level ladder shown by `/think`. - * - * @deprecated Prefer `resolveThinkingProfile`. - */ - isBinaryThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | undefined; - /** - * Provider-owned xhigh reasoning support. - * - * Return true only for models that should expose the `xhigh` thinking level. - * - * @deprecated Prefer `resolveThinkingProfile`. - */ - supportsXHighThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | 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 default thinking level. - * - * Use this to keep model-family defaults (for example Claude 4.6 => - * adaptive) out of core command logic. - * - * @deprecated Prefer `resolveThinkingProfile`. - */ - resolveDefaultThinkingLevel?: ( - ctx: ProviderDefaultThinkingPolicyContext, - ) => "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive" | 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; - /** - * @deprecated Declare `contracts.externalAuthProviders` in the plugin manifest - * and implement `resolveExternalAuthProfiles` instead. Kept at the public - * plugin boundary until the SDK removal window closes. - */ - resolveExternalOAuthProfiles?: ( - ctx: ProviderResolveExternalOAuthProfilesContext, - ) => - | 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; -}; - -/** Speech capability registered by a plugin. */ -export type SpeechProviderPlugin = { - id: SpeechProviderId; - label: string; - aliases?: string[]; - autoSelectOrder?: number; - /** Default provider operation timeout in milliseconds when caller/config omit timeoutMs. */ - defaultTimeoutMs?: number; - defaultModel?: string; - models?: readonly string[]; - voices?: readonly string[]; - resolveConfig?: (ctx: SpeechProviderResolveConfigContext) => SpeechProviderConfig; - parseDirectiveToken?: (ctx: SpeechDirectiveTokenParseContext) => SpeechDirectiveTokenParseResult; - resolveTalkConfig?: (ctx: SpeechProviderResolveTalkConfigContext) => SpeechProviderConfig; - resolveTalkOverrides?: ( - ctx: SpeechProviderResolveTalkOverridesContext, - ) => SpeechProviderConfig | undefined; - prepareSynthesis?: ( - ctx: SpeechProviderPrepareSynthesisContext, - ) => - | SpeechProviderPreparedSynthesis - | undefined - | Promise; - isConfigured: (ctx: SpeechProviderConfiguredContext) => boolean; - synthesize: (req: SpeechSynthesisRequest) => Promise; - streamSynthesize?: (req: SpeechSynthesisStreamRequest) => Promise; - synthesizeTelephony?: ( - req: SpeechTelephonySynthesisRequest, - ) => Promise; - listVoices?: (req: SpeechListVoicesRequest) => Promise; -}; - -export type PluginSpeechProviderEntry = SpeechProviderPlugin & { - pluginId: string; -}; - -/** Realtime transcription capability registered by a plugin. */ -export type RealtimeTranscriptionProviderPlugin = { - id: RealtimeTranscriptionProviderId; - label: string; - aliases?: string[]; - defaultModel?: string; - models?: readonly string[]; - autoSelectOrder?: number; - resolveConfig?: ( - ctx: RealtimeTranscriptionProviderResolveConfigContext, - ) => RealtimeTranscriptionProviderConfig; - isConfigured: (ctx: RealtimeTranscriptionProviderConfiguredContext) => boolean; - createSession: (req: RealtimeTranscriptionSessionCreateRequest) => RealtimeTranscriptionSession; -}; - -export type PluginRealtimeTranscriptionProviderEntry = RealtimeTranscriptionProviderPlugin & { - pluginId: string; -}; - -/** Transcript source capability registered by a channel or meeting plugin. */ -export type TranscriptSourceProvider = TranscriptsSourceProviderCapability; - -export type PluginTranscriptsSourceProviderEntry = TranscriptSourceProvider & { - pluginId: string; -}; - -/** Realtime voice capability registered by a plugin. */ -export type RealtimeVoiceProviderPlugin = { - id: RealtimeVoiceProviderId; - label: string; - aliases?: string[]; - defaultModel?: string; - models?: readonly string[]; - autoSelectOrder?: number; - capabilities?: RealtimeVoiceProviderCapabilities; - resolveConfig?: (ctx: RealtimeVoiceProviderResolveConfigContext) => RealtimeVoiceProviderConfig; - isConfigured: (ctx: RealtimeVoiceProviderConfiguredContext) => boolean; - createBridge: (req: RealtimeVoiceBridgeCreateRequest) => RealtimeVoiceBridge; - createBrowserSession?: ( - req: RealtimeVoiceBrowserSessionCreateRequest, - ) => Promise; -}; - -export type PluginRealtimeVoiceProviderEntry = RealtimeVoiceProviderPlugin & { - pluginId: string; -}; - -export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider; -export type ImageGenerationProviderPlugin = ImageGenerationProvider; -export type VideoGenerationProviderPlugin = VideoGenerationProvider; -export type MusicGenerationProviderPlugin = MusicGenerationProvider; - -export type OpenClawPluginGatewayMethod = { - method: string; - handler: GatewayRequestHandler; -}; - -// ============================================================================= -// Plugin Commands -// ============================================================================= - -export type PluginCommandDiagnosticsSession = { - /** Stable host session key when available. */ - sessionKey?: string; - /** Ephemeral OpenClaw session id when available. */ - sessionId?: string; - /** - * Deprecated transcript locator for this OpenClaw session when available. - * - * SQLite-backed sessions use a `sqlite:::` - * marker, not a filesystem path. Use session id/key plus transcript-runtime - * helpers for active transcript reads. - * - * @deprecated Use session identity fields with `plugin-sdk/session-transcript-runtime`. - */ - sessionFile?: string; - /** Embedded agent harness selected for this session. */ - agentHarnessId?: string; - /** Channel/provider for this session when available. */ - channel?: string; - /** Provider channel id when available. */ - channelId?: ChannelId; - /** Account id for multi-account channels when available. */ - accountId?: string; - /** Thread/topic id when available. */ - messageThreadId?: string | number; - /** Parent conversation id for thread-capable channels when available. */ - threadParentId?: string; -}; - -/** - * Context passed to plugin command handlers. - */ -export type PluginCommandContext = { - /** The sender's identifier (for example a channel-scoped user ID) */ - senderId?: string; - /** The channel/surface (for example "chat" or "team-chat") */ - channel: string; - /** Provider channel id */ - channelId?: ChannelId; - /** Whether the sender is on the allowlist */ - isAuthorizedSender: boolean; - /** Whether the sender is an owner for owner-only command surfaces. */ - senderIsOwner?: boolean; - /** Gateway client scopes for internal control-plane callers */ - gatewayClientScopes?: string[]; - /** Host-resolved agent that owns the active session. */ - agentId?: string; - /** Stable host session key for the active conversation when available. */ - sessionKey?: string; - /** Ephemeral host session id for the active conversation when available. */ - sessionId?: string; - /** - * Deprecated transcript locator for the active OpenClaw session when available. - * - * SQLite-backed sessions use a `sqlite:::` - * marker, not a filesystem path. Use session id/key plus transcript-runtime - * helpers for active transcript reads. - * - * @deprecated Use session identity fields with `plugin-sdk/session-transcript-runtime`. - */ - sessionFile?: string; - /** Raw command arguments after the command name */ - args?: string; - /** The full normalized command body */ - commandBody: string; - /** Current OpenClaw configuration */ - config: OpenClawConfig; - /** Raw "From" value (channel-scoped id) */ - from?: string; - /** Raw "To" value (channel-scoped id) */ - to?: string; - /** Account id for multi-account channels */ - accountId?: string; - /** Thread/topic id if available */ - messageThreadId?: string | number; - /** Parent conversation id for thread-capable channels */ - threadParentId?: string; - /** Sensitive diagnostics-only session inventory for owner-gated commands. */ - diagnosticsSessions?: PluginCommandDiagnosticsSession[]; - /** Host-bound runtime capabilities scoped to this command invocation. */ - runtimeContext?: { - llm?: Pick; - }; - /** Internal diagnostics-only marker that exec approval already authorized upload. */ - diagnosticsUploadApproved?: boolean; - /** Internal diagnostics-only marker to preview upload effects without exposing ids. */ - diagnosticsPreviewOnly?: boolean; - /** Internal diagnostics-only marker for owner-private routed confirmations. */ - diagnosticsPrivateRouted?: boolean; - requestConversationBinding: ( - params?: PluginConversationBindingRequestParams, - ) => Promise; - detachConversationBinding: () => Promise<{ removed: boolean }>; - getCurrentConversationBinding: () => Promise; -}; - -/** - * Result returned by a plugin command handler. - */ -export type PluginCommandResult = ReplyPayload & { - /** Allows the agent session to continue processing after the command. */ - continueAgent?: boolean; - /** Suppresses channel fallback replies when the handler already delivered a response. */ - suppressReply?: boolean; -}; - -/** - * Handler function for plugin commands. - */ -export type PluginCommandHandler = ( - ctx: PluginCommandContext, -) => PluginCommandResult | Promise; - -/** - * Definition for a plugin-registered command. - */ -export const AGENT_PROMPT_SURFACE_KINDS = [ - "openclaw_main", - /** @deprecated Use openclaw_main. */ - "pi_main", - "codex_app_server", - "cli_backend", - "acp_backend", - "subagent", -] as const; - -export type AgentPromptSurfaceKind = (typeof AGENT_PROMPT_SURFACE_KINDS)[number]; - -export type AgentPromptGuidanceEntry = { - text: string; - surfaces?: readonly AgentPromptSurfaceKind[]; -}; - -export type AgentPromptGuidance = string | AgentPromptGuidanceEntry; - -export type OpenClawPluginCommandDefinition = { - /** Command name without leading slash (e.g., "tts") */ - name: string; - /** - * Optional native-command aliases for slash/menu surfaces. - * `default` applies to all native providers unless a provider-specific - * override exists (for example `{ default: "talkvoice", teamChat: "voice2" }`). - */ - nativeNames?: Partial> & { default?: string }; - /** - * Optional native progress placeholder text for native command surfaces. - * `default` applies to all native providers unless a provider-specific - * override exists. - */ - nativeProgressMessages?: Partial> & { - default?: string; - }; - /** Description shown in /help and command menus */ - description: string; - /** Localized descriptions for native command surfaces that support them. */ - descriptionLocalizations?: Record; - /** - * Optional channel ids this command belongs to. - * Omit to keep the command available on every channel surface. - */ - channels?: readonly string[]; - /** Optional system-prompt guidance for agents when this command is registered. */ - agentPromptGuidance?: readonly AgentPromptGuidance[]; - /** Whether this command accepts arguments */ - acceptsArgs?: boolean; - /** Whether only authorized senders can use this command (default: true) */ - requireAuth?: boolean; - /** Operator scopes required by gateway clients; command owners may satisfy this on chat surfaces. */ - requiredScopes?: OperatorScope[]; - /** Whether a trusted bundled handler needs owner status for subcommand-level authorization. */ - exposeSenderIsOwner?: boolean; - /** - * Allows a bundled plugin to claim a command name that is otherwise reserved - * by core. External plugins cannot use this field. - */ - ownership?: "plugin" | "reserved"; - /** The handler function */ - handler: PluginCommandHandler; -}; - -export type PluginInteractiveHandlerResult = { - handled?: boolean; -} | void; - -export type PluginInteractiveRegistration< - TContext = unknown, - TChannel extends string = string, - TResult = PluginInteractiveHandlerResult, -> = { - channel: TChannel; - namespace: string; - handler: (ctx: TContext) => Promise | TResult; -}; - -export type PluginInteractiveHandlerRegistration = PluginInteractiveRegistration; - -export type OpenClawPluginHttpRouteAuth = "gateway" | "plugin"; -export type OpenClawPluginHttpRouteMatch = "exact" | "prefix"; -export type OpenClawPluginGatewayRuntimeScopeSurface = "write-default" | "trusted-operator"; - -export type OpenClawPluginHttpRouteHandler = ( - req: IncomingMessage, - res: ServerResponse, -) => Promise | boolean | void; - -export type OpenClawPluginHttpRouteUpgradeHandler = ( - req: IncomingMessage, - socket: Duplex, - head: Buffer, -) => Promise | boolean | void; - -export type OpenClawPluginHttpRouteParams = { - path: string; - handler: OpenClawPluginHttpRouteHandler; - handleUpgrade?: OpenClawPluginHttpRouteUpgradeHandler; - auth: OpenClawPluginHttpRouteAuth; - match?: OpenClawPluginHttpRouteMatch; - gatewayRuntimeScopeSurface?: OpenClawPluginGatewayRuntimeScopeSurface; - nodeCapability?: { - surface: string; - ttlMs?: number; - }; - replaceExisting?: boolean; -}; - -export type OpenClawPluginHostedMediaResolver = ( - mediaUrl: string, -) => string | null | undefined | Promise; - -export type OpenClawPluginCliContext = { - /** - * Command object where this plugin should register its commands. - * - * For root CLI registrations this is the root `openclaw` program. For nested - * registrations it is the resolved parent command from `parentPath`. - */ - program: Command; - parentPath: readonly string[]; - config: OpenClawConfig; - workspaceDir?: string; - logger: PluginLogger; -}; - -export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise; - -/** - * Top-level CLI metadata for plugin-owned commands. - * - * Descriptors are the parse-time contract for lazy plugin CLI registration. - * If you want OpenClaw to keep a plugin command lazy-loaded while still - * advertising it at the root CLI level, provide descriptors that cover every - * top-level command root registered by that plugin CLI surface. - */ -export type OpenClawPluginCliCommandDescriptor = { - name: string; - description: string; - hasSubcommands: boolean; -}; - -export type OpenClawPluginNodeCliFeatureOptions = { - /** Explicit node feature command names owned under `openclaw nodes`. */ - commands?: string[]; - /** - * Parse-time command descriptors for lazy node feature CLI registration. - * - * Descriptors are registered under `openclaw nodes`, so a descriptor named - * `"camera"` exposes `openclaw nodes camera`. - */ - descriptors?: OpenClawPluginCliCommandDescriptor[]; -}; - -export type OpenClawPluginReloadRegistration = { - restartPrefixes?: string[]; - hotPrefixes?: string[]; - noopPrefixes?: string[]; -}; - -export type { - OpenClawPluginNodeHostCommand, - OpenClawPluginNodeHostCommandAvailabilityContext, - OpenClawPluginNodeHostCommandIo, -} from "./types.node-host.js"; - -export type OpenClawPluginNodeInvokeTransportResult = - | { - ok: true; - payload?: unknown; - payloadJSON?: string | null; - } - | { - ok: false; - code?: string; - message: string; - details?: Record; - }; - -export type OpenClawPluginNodeInvokeApprovalDecision = "allow-once" | "allow-always" | "deny"; - -export type OpenClawPluginNodeInvokePolicyApprovalRuntime = { - request: (input: { - title: string; - description: string; - severity?: "info" | "warning" | "critical"; - toolName?: string; - toolCallId?: string; - agentId?: string; - sessionKey?: string; - timeoutMs?: number; - }) => Promise<{ - id?: string; - decision?: OpenClawPluginNodeInvokeApprovalDecision | null; - }>; -}; - -export type OpenClawPluginNodeInvokePolicyContext = { - nodeId: string; - command: string; - params: unknown; - timeoutMs?: number; - idempotencyKey?: string; - config: OpenClawConfig; - pluginConfig?: Record; - node?: { - nodeId: string; - displayName?: string; - platform?: string; - deviceFamily?: string; - commands?: string[]; - }; - client?: { - connId?: string; - scopes?: string[]; - } | null; - approvals?: OpenClawPluginNodeInvokePolicyApprovalRuntime; - invokeNode: (input?: { - params?: unknown; - timeoutMs?: number; - idempotencyKey?: string; - }) => Promise; -}; - -export type OpenClawPluginNodeInvokePolicyResult = - | { - ok: true; - payload?: unknown; - payloadJSON?: string | null; - } - | { - ok: false; - message: string; - code?: string; - details?: Record; - unavailable?: boolean; - }; - -export type OpenClawPluginNodeInvokePolicy = { - commands: string[]; - /** - * Platforms where these node-handled commands should be allowlisted by default. - * Omit for commands that require explicit `gateway.nodes.allowCommands`. - */ - defaultPlatforms?: Array<"ios" | "android" | "macos" | "windows" | "linux" | "unknown">; - /** - * Dangerous policy commands are filtered out of default allowlists unless - * explicitly allowed by config. - */ - dangerous?: boolean; - /** - * iOS foreground-restricted commands should be queued for foreground delivery - * when an iOS node reports BACKGROUND_UNAVAILABLE. - */ - foregroundRestrictedOnIos?: boolean; - handle: ( - ctx: OpenClawPluginNodeInvokePolicyContext, - ) => Promise | OpenClawPluginNodeInvokePolicyResult; -}; - -export type OpenClawPluginSecurityAuditContext = { - config: OpenClawConfig; - sourceConfig: OpenClawConfig; - env: NodeJS.ProcessEnv; - stateDir: string; - configPath: string; -}; - -export type OpenClawPluginSecurityAuditCollector = ( - ctx: OpenClawPluginSecurityAuditContext, -) => SecurityAuditFinding[] | Promise; - -export type OpenClawGatewayDiscoveryAdvertiseContext = { - machineDisplayName: string; - gatewayPort: number; - gatewayTlsEnabled: boolean; - gatewayTlsFingerprintSha256?: string; - gatewayDirectReachable: boolean; - canvasPort?: number; - tailnetDns?: string; - sshPort?: number; - cliPath?: string; - minimal: boolean; -}; - -export type OpenClawGatewayDiscoveryService = { - id: string; - advertise: ( - ctx: OpenClawGatewayDiscoveryAdvertiseContext, - ) => void | Promise void | Promise }>; -}; - -/** Context passed to long-lived plugin services. */ -export type OpenClawPluginServiceContext = { - config: OpenClawConfig; - workspaceDir?: string; - stateDir: string; - logger: PluginLogger; - gatewayEvents?: import("./gateway-events.js").OpenClawPluginGatewayEvents; - startupTrace?: { - detail?: (name: string, metrics: ReadonlyArray) => void; - measure: (name: string, run: () => T | Promise) => Promise; - }; - internalDiagnostics?: { - emit: (event: DiagnosticEventInput, privateData?: DiagnosticEventPrivateData) => void; - onEvent: ( - listener: ( - event: DiagnosticEventPayload, - metadata: DiagnosticEventMetadata, - privateData: DiagnosticEventPrivateData, - ) => void, - ) => () => void; - }; -}; - -/** Background service registered by a plugin during `register(api)`. */ -export type OpenClawPluginService = { - id: string; - start: (ctx: OpenClawPluginServiceContext) => void | Promise; - stop?: (ctx: OpenClawPluginServiceContext) => void | Promise; -}; - -export type OpenClawPluginChannelRegistration = { - plugin: ChannelPlugin; -}; - -/** Module-level plugin definition loaded from a native plugin entry file. */ -export type OpenClawPluginDefinition = { - id?: string; - name?: string; - description?: string; - version?: string; - /** - * @deprecated Declare exclusive plugin kind in `openclaw.plugin.json` via - * manifest `kind`. Runtime-exported `kind` is kept as a compatibility - * fallback for older plugins and may require loading plugin runtime on - * metadata-only command paths. - */ - kind?: PluginKind | PluginKind[]; - configSchema?: OpenClawPluginConfigSchema; - reload?: OpenClawPluginReloadRegistration; - nodeHostCommands?: OpenClawPluginNodeHostCommand[]; - securityAuditCollectors?: OpenClawPluginSecurityAuditCollector[]; - register?: (api: OpenClawPluginApi) => void; - activate?: (api: OpenClawPluginApi) => void; -}; - -export type OpenClawPluginModule = OpenClawPluginDefinition | ((api: OpenClawPluginApi) => void); - -/** - * Public label exposed to plugin `register(api)` calls. - * - * Keep this as a compatibility signal for plugin authors. Loader internals - * should derive explicit capability booleans from the mode instead of branching - * on raw strings throughout the code path. - * - * - `full`: live runtime activation; long-lived side effects may start. - * - `discovery`: read-only capability discovery; skip sockets/workers/clients. - * - `tool-discovery`: capability discovery for executable tools; skip channel runtime hydration. - * - `setup-only`: lightweight channel setup entry only. - * - `setup-runtime`: setup flow that also needs the runtime channel entry. - * - `cli-metadata`: CLI command metadata collection. - */ -export type PluginRegistrationMode = - | "full" - | "discovery" - | "tool-discovery" - | "setup-only" - | "setup-runtime" - | "cli-metadata"; - -export type PluginConfigMigration = (config: OpenClawConfig) => - | { - config: OpenClawConfig; - changes: string[]; - } - | null - | undefined; - -export type MigrationItemStatus = - | "planned" - | "migrated" - | "skipped" - | "warning" - | "conflict" - | "error"; -export type MigrationItemKind = - | "auth" - | "config" - | "secret" - | "memory" - | "skill" - | "workspace" - | "session" - | "file" - | "archive" - | "manual"; -export type MigrationItemAction = - | "copy" - | "create" - | "update" - | "merge" - | "append" - | "archive" - | "skip" - | "manual"; - -export type MigrationItem = { - id: string; - kind: MigrationItemKind | (string & {}); - action: MigrationItemAction | (string & {}); - status: MigrationItemStatus; - source?: string; - target?: string; - message?: string; - reason?: string; - sensitive?: boolean; - /** Core-owned source revision bound by reviewed embedded migration flows. */ - sourceRevision?: { algorithm: "sha256"; digest: string }; - details?: Record; -}; - -export type MigrationSummary = { - total: number; - planned: number; - migrated: number; - skipped: number; - conflicts: number; - errors: number; - sensitive: number; -}; - -export type MigrationDetection = { - found: boolean; - source?: string; - label?: string; - confidence?: "low" | "medium" | "high"; - message?: string; -}; - -export type MigrationPlan = { - providerId: string; - source: string; - target?: string; - summary: MigrationSummary; - items: MigrationItem[]; - warnings?: string[]; - nextSteps?: string[]; - metadata?: Record; -}; - -export type MigrationApplyResult = MigrationPlan & { - backupPath?: string; - reportDir?: string; -}; - -export type MigrationProviderPreparation = { - dispose?: () => void | Promise; -}; - -export type MigrationProviderContext = { - config: OpenClawConfig; - runtime?: PluginRuntime; - logger: PluginLogger; - stateDir: string; - /** Explicit destination agent for embedded migration surfaces such as Control UI. */ - targetAgentId?: string; - /** Optional item-kind scope used by embedded migration surfaces to avoid unrelated discovery. */ - itemKinds?: readonly string[]; - source?: string; - includeSecrets?: boolean; - overwrite?: boolean; - providerOptions?: Record; - backupPath?: string; - reportDir?: string; - signal?: AbortSignal; -}; - -/** Migration source implemented by a plugin and orchestrated by `openclaw migrate`. */ -export type MigrationProviderPlugin = { - id: string; - label: string; - description?: string; - /** Item kinds this provider can expose without requiring a full plan. */ - supportedItemKinds?: readonly string[]; - detect?: (ctx: MigrationProviderContext) => MigrationDetection | Promise; - prepareApply?: ( - ctx: MigrationProviderContext, - ) => MigrationProviderPreparation | Promise | undefined; - plan: (ctx: MigrationProviderContext) => MigrationPlan | Promise; - apply: ( - ctx: MigrationProviderContext, - plan?: MigrationPlan, - ) => MigrationApplyResult | Promise; -}; - -export type PluginSetupAutoEnableContext = { - config: OpenClawConfig; - env: NodeJS.ProcessEnv; -}; - -export type PluginSetupAutoEnableProbe = ( - ctx: PluginSetupAutoEnableContext, -) => string | string[] | null | undefined; - -export type OpenClawPluginSessionStateApi = { - /** Register plugin-owned session state projected into Gateway session rows. */ - registerSessionExtension: (extension: PluginSessionExtensionRegistration) => void; -}; - -export type OpenClawPluginSessionWorkflowApi = { - /** Queue one plugin-owned context injection for the next agent turn in a session. */ - enqueueNextTurnInjection: ( - injection: PluginNextTurnInjection, - ) => Promise; - /** - * Register cleanup metadata for a plugin-owned session scheduler job. - * This does not schedule work or create task records; it only lets the host - * clean external scheduler state during reset/delete/disable. - */ - registerSessionSchedulerJob: ( - job: PluginSessionSchedulerJobRegistration, - ) => PluginSessionSchedulerJobHandle | undefined; - /** Send host-validated files to the active direct-outbound route for a session. */ - sendSessionAttachment: ( - params: PluginSessionAttachmentParams, - ) => Promise; - /** - * Schedule a future agent turn in a session through Cron. - * Cron owns timing and creates the task ledger entry when the turn runs. - */ - scheduleSessionTurn: ( - params: PluginSessionTurnScheduleParams, - ) => Promise; - /** Remove Cron-backed scheduled session turns that share a plugin-owned tag. */ - unscheduleSessionTurnsByTag: ( - params: PluginSessionTurnUnscheduleByTagParams, - ) => Promise; -}; - -export type OpenClawPluginSessionControlsApi = { - /** Register a typed session action that clients can dispatch through the Gateway. */ - registerSessionAction: (action: PluginSessionActionRegistration) => void; - /** Register a generic Control UI contribution descriptor. */ - registerControlUiDescriptor: (descriptor: PluginControlUiDescriptor) => void; -}; - -export type OpenClawPluginSessionApi = { - state: OpenClawPluginSessionStateApi; - workflow: OpenClawPluginSessionWorkflowApi; - controls: OpenClawPluginSessionControlsApi; -}; - -export type OpenClawPluginAgentEventsApi = { - /** Subscribe to sanitized agent events through the host-owned plugin lifecycle. */ - registerAgentEventSubscription: (subscription: PluginAgentEventSubscriptionRegistration) => void; - /** Emit a host-routed, plugin-attributed event for workflow/UI subscribers. */ - emitAgentEvent: (params: PluginAgentEventEmitParams) => PluginAgentEventEmitResult; -}; - -export type OpenClawPluginAgentApi = { - events: OpenClawPluginAgentEventsApi; -}; - -export type OpenClawPluginRunContextApi = { - /** Store namespaced, JSON-compatible data for the active run. Cleared on run end/error. */ - setRunContext: (patch: PluginRunContextPatch) => boolean; - /** Read namespaced plugin data for a run. */ - getRunContext: (params: PluginRunContextGetParams) => PluginJsonValue | undefined; - /** Clear one namespace or all namespaces this plugin owns for a run. */ - clearRunContext: (params: { runId: string; namespace?: string }) => void; -}; - -export type OpenClawPluginLifecycleApi = { - /** Register cleanup hooks for plugin-owned host state and background work. */ - registerRuntimeLifecycle: (lifecycle: PluginRuntimeLifecycleRegistration) => void; -}; - -/** Main registration API injected into native plugin entry files. */ -export type OpenClawPluginApi = { - id: string; - name: string; - version?: string; - description?: string; - source: string; - rootDir?: string; - registrationMode: PluginRegistrationMode; - config: OpenClawConfig; - pluginConfig?: Record; - /** - * In-process runtime helpers for trusted native plugins. - * - * This surface is broader than hooks. Prefer hooks for third-party - * automation/integration unless you need native registry integration. - */ - runtime: PluginRuntime; - logger: PluginLogger; - /** - * Grouped facade over the existing flat session-related plugin API. - * Flat methods remain supported for compatibility. - */ - session: OpenClawPluginSessionApi; - /** Grouped facade for agent-event workflow seams. */ - agent: OpenClawPluginAgentApi; - /** Grouped facade for run-scoped plugin scratch state. */ - runContext: OpenClawPluginRunContextApi; - /** Grouped facade for plugin-owned lifecycle cleanup hooks. */ - lifecycle: OpenClawPluginLifecycleApi; - registerTool: ( - tool: AnyAgentTool | OpenClawPluginToolFactory, - opts?: OpenClawPluginToolOptions, - ) => void; - registerHook: ( - events: string | string[], - handler: InternalHookHandler, - opts?: OpenClawPluginHookOptions, - ) => void; - registerHttpRoute: (params: OpenClawPluginHttpRouteParams) => void; - /** Register a plugin-owned resolver for browser-style hosted media URLs. */ - registerHostedMediaResolver: (resolver: OpenClawPluginHostedMediaResolver) => void; - /** Bind a declared MCP server's transport to the trusted message requester. */ registerMcpServerConnectionResolver: ( - resolver: import("./types.mcp-connection.js").OpenClawPluginMcpServerConnectionResolver, - ) => void; - /** Register a native messaging channel plugin (channel capability). */ - registerChannel: (registration: OpenClawPluginChannelRegistration | ChannelPlugin) => void; - /** - * Register a gateway RPC method for this plugin. - * - * Reserved core admin namespaces (`config.*`, `exec.approvals.*`, - * `wizard.*`, `update.*`) always normalize to `operator.admin` even if a - * narrower scope is requested. - */ - registerGatewayMethod: ( - method: string, - handler: GatewayRequestHandler, - opts?: { scope?: OperatorScope }, - ) => void; - /** Register a read-only external-session catalog with optional native adoption actions. */ - registerSessionCatalog: (provider: SessionCatalogProvider) => void; - registerCli: ( - registrar: OpenClawPluginCliRegistrar, - opts?: { - /** Parent command path for nested command groups, for example `["nodes"]`. */ - parentPath?: string[]; - /** Explicit command names owned by this registrar at `parentPath`. */ - commands?: string[]; - /** - * Parse-time command descriptors for lazy CLI registration. - * - * When descriptors cover every command exposed at `parentPath`, OpenClaw - * can keep the plugin registrar lazy. Command-only registrations stay on - * the eager compatibility path. - */ - descriptors?: OpenClawPluginCliCommandDescriptor[]; - }, - ) => void; - /** - * Register a plugin-owned node feature command group under `openclaw nodes`. - * - * This is equivalent to `registerCli(registrar, { parentPath: ["nodes"], ... })` - * and is intended for paired-node capabilities such as camera, screen, or Canvas. - */ - registerNodeCliFeature: ( - registrar: OpenClawPluginCliRegistrar, - opts?: OpenClawPluginNodeCliFeatureOptions, - ) => void; - registerReload: (registration: OpenClawPluginReloadRegistration) => void; - registerNodeHostCommand: (command: OpenClawPluginNodeHostCommand) => void; - registerNodeInvokePolicy: (policy: OpenClawPluginNodeInvokePolicy) => void; - registerSecurityAuditCollector: (collector: OpenClawPluginSecurityAuditCollector) => void; - registerService: (service: OpenClawPluginService) => void; - /** Register a local gateway discovery advertiser such as mDNS/Bonjour. */ - registerGatewayDiscoveryService: (service: OpenClawGatewayDiscoveryService) => void; - /** Register a text-only CLI backend used by the local CLI runner. */ - registerCliBackend: (backend: CliBackendPlugin) => void; - /** Register plugin-owned prompt/message compatibility text transforms. */ - registerTextTransforms: (transforms: PluginTextTransformRegistration) => void; - /** Register a lightweight config migration that can run before plugin runtime loads. */ - registerConfigMigration: (migrate: PluginConfigMigration) => void; - /** Register an importer for `openclaw migrate` (migration capability). */ - registerMigrationProvider: (provider: MigrationProviderPlugin) => void; - /** Register a lightweight config probe that can auto-enable this plugin generically. */ - registerAutoEnableProbe: (probe: PluginSetupAutoEnableProbe) => void; - /** Register a native model/provider plugin (text inference capability). */ - registerProvider: (provider: ProviderPlugin) => void; - /** Register a cloud-worker lifecycle provider. */ - registerWorkerProvider: (provider: WorkerProvider) => void; - /** Register provider-owned model catalog rows for text and media generation. */ - registerModelCatalogProvider: (provider: UnifiedModelCatalogProviderPlugin) => void; - /** Register a general embedding provider (embedding capability). */ - registerEmbeddingProvider: ( - adapter: import("./embedding-providers.js").EmbeddingProviderAdapter, - ) => void; - /** Register a speech synthesis provider (speech capability). */ - registerSpeechProvider: (provider: SpeechProviderPlugin) => void; - /** Register a realtime transcription provider (streaming STT capability). */ - registerRealtimeTranscriptionProvider: (provider: RealtimeTranscriptionProviderPlugin) => void; - /** Register a realtime voice provider (duplex voice capability). */ - registerRealtimeVoiceProvider: (provider: RealtimeVoiceProviderPlugin) => void; - /** Register a media understanding provider (media understanding capability). */ - registerMediaUnderstandingProvider: (provider: MediaUnderstandingProviderPlugin) => void; - /** Register a transcripts source provider (live or imported meeting transcript capability). */ - registerTranscriptSourceProvider: (provider: TranscriptSourceProvider) => void; - /** Register an image generation provider (image generation capability). */ - registerImageGenerationProvider: (provider: ImageGenerationProviderPlugin) => void; - /** Register a video generation provider (video generation capability). */ - registerVideoGenerationProvider: (provider: VideoGenerationProviderPlugin) => void; - /** Register a music generation provider (music generation capability). */ - registerMusicGenerationProvider: (provider: MusicGenerationProviderPlugin) => void; - /** Register a web fetch provider (web fetch capability). */ - registerWebFetchProvider: (provider: WebFetchProviderPlugin) => void; - /** Register a web search provider (web search capability). */ - registerWebSearchProvider: (provider: WebSearchProviderPlugin) => void; - registerInteractiveHandler: (registration: PluginInteractiveHandlerRegistration) => void; - onConversationBindingResolved: ( - handler: (event: PluginConversationBindingResolvedEvent) => void | Promise, - ) => void; - /** - * Register a custom command that bypasses the LLM agent. - * Plugin commands are processed before built-in commands and before agent invocation. - * Use this for simple state-toggling or status commands that don't need AI reasoning. - */ - registerCommand: (command: OpenClawPluginCommandDefinition) => void; - /** Register a context engine implementation (exclusive slot - only one active at a time). */ - registerContextEngine: ( - id: string, - factory: import("../context-engine/registry.js").ContextEngineFactory, - ) => void; - /** Register a compaction provider (pluggable summarization backend). */ - registerCompactionProvider: ( - provider: import("./compaction-provider.js").CompactionProvider, - ) => void; - /** Register an agent harness implementation. */ - registerAgentHarness: (harness: AgentHarness) => void; - /** - * Register a Codex app-server extension factory for Codex harness tool-result - * middleware. Only bundled plugins may use this seam, and - * `contracts.embeddedExtensionFactories` must include `"codex-app-server"`. - */ - registerCodexAppServerExtensionFactory: (factory: CodexAppServerExtensionFactory) => void; - /** - * Register runtime-neutral tool-result middleware. Declare - * `contracts.agentToolResultMiddleware` for every targeted runtime. - */ - registerAgentToolResultMiddleware: ( - handler: AgentToolResultMiddleware, - options?: AgentToolResultMiddlewareOptions, - ) => void; - /** - * Register plugin-owned session state that can be projected into Gateway session rows. - * @deprecated Use `api.session.state.registerSessionExtension(...)`. - */ - registerSessionExtension: (extension: PluginSessionExtensionRegistration) => void; - /** - * Queue one plugin-owned context injection for the next agent turn in a session. - * @deprecated Use `api.session.workflow.enqueueNextTurnInjection(...)`. - */ - enqueueNextTurnInjection: ( - injection: PluginNextTurnInjection, - ) => Promise; - /** - * Register a trusted pre-tool policy. Installed plugins must declare the - * policy id in `contracts.trustedToolPolicies`. - */ - registerTrustedToolPolicy: (policy: PluginTrustedToolPolicyRegistration) => void; - /** - * Register display/policy metadata for a plugin-owned tool. Metadata is - * scoped to the (pluginId, toolName) pair at projection time, so plugins - * cannot decorate other plugins' tools or core tools through this surface. - */ - registerToolMetadata: (metadata: PluginToolMetadataRegistration) => void; - /** - * Register a generic Control UI contribution descriptor. - * @deprecated Use `api.session.controls.registerControlUiDescriptor(...)`. - */ - registerControlUiDescriptor: (descriptor: PluginControlUiDescriptor) => void; - /** - * Register cleanup hooks for plugin-owned host state and background work. - * @deprecated Use `api.lifecycle.registerRuntimeLifecycle(...)`. - */ - registerRuntimeLifecycle: (lifecycle: PluginRuntimeLifecycleRegistration) => void; - /** - * Subscribe to sanitized agent events through the host-owned plugin lifecycle. - * @deprecated Use `api.agent.events.registerAgentEventSubscription(...)`. - */ - registerAgentEventSubscription: (subscription: PluginAgentEventSubscriptionRegistration) => void; - /** - * Emit a host-routed, plugin-attributed agent event for workflow/UI subscribers. - * @deprecated Use `api.agent.events.emitAgentEvent(...)`. - */ - emitAgentEvent: (params: PluginAgentEventEmitParams) => PluginAgentEventEmitResult; - /** - * Store namespaced, JSON-compatible data for the active run. Cleared on run end/error. - * @deprecated Use `api.runContext.setRunContext(...)`. - */ - setRunContext: (patch: PluginRunContextPatch) => boolean; - /** - * Read namespaced plugin data for a run. - * @deprecated Use `api.runContext.getRunContext(...)`. - */ - getRunContext: (params: PluginRunContextGetParams) => PluginJsonValue | undefined; - /** - * Clear one namespace or all namespaces this plugin owns for a run. - * @deprecated Use `api.runContext.clearRunContext(...)`. - */ - clearRunContext: (params: { runId: string; namespace?: string }) => void; - /** - * Register cleanup metadata for a plugin-owned session scheduler job. - * This does not schedule work or create task records; it only lets the host - * clean external scheduler state during reset/delete/disable. - * - * @deprecated Use `api.session.workflow.registerSessionSchedulerJob(...)`. - */ - registerSessionSchedulerJob: ( - job: PluginSessionSchedulerJobRegistration, - ) => PluginSessionSchedulerJobHandle | undefined; - /** - * Register a typed session action that clients can dispatch through the Gateway. - * @deprecated Use `api.session.controls.registerSessionAction(...)`. - */ - registerSessionAction: (action: PluginSessionActionRegistration) => void; - /** - * Send one or more host-validated files to the active direct-outbound channel for a session. - * - * This API is intended for bundled plugins running with the host channel/session - * integration available. Calls may resolve to `{ ok: false }` instead of attaching - * files when global side effects are disabled or when the required plugin/channel - * runtime is not loaded, so callers must handle rejection via the returned result. - * - * @deprecated Use `api.session.workflow.sendSessionAttachment(...)`. - */ - sendSessionAttachment: ( - params: PluginSessionAttachmentParams, - ) => Promise; - /** - * Schedule a future agent turn in a session through Cron. - * Cron owns timing and creates the task ledger entry when the turn runs. - * Bundled plugins only; workspace plugins receive undefined. - * - * @deprecated Use `api.session.workflow.scheduleSessionTurn(...)`. - */ - scheduleSessionTurn: ( - params: PluginSessionTurnScheduleParams, - ) => Promise; - /** - * Remove Cron-backed scheduled session turns that share the same plugin-owned tag. - * Bundled plugins only; workspace plugins receive a zero-count result. - * - * @deprecated Use `api.session.workflow.unscheduleSessionTurnsByTag(...)`. - */ - unscheduleSessionTurnsByTag: ( - params: PluginSessionTurnUnscheduleByTagParams, - ) => Promise; - /** Register the active detached task runtime for this plugin (exclusive slot). */ - registerDetachedTaskRuntime: ( - runtime: import("./runtime/runtime-tasks.types.js").DetachedTaskLifecycleRuntime, - ) => void; - /** Register the active memory capability for this memory plugin (exclusive slot). */ - registerMemoryCapability: ( - capability: import("./memory-state.js").MemoryPluginCapability, - ) => void; - /** - * Register the system prompt section builder for this memory plugin (exclusive slot). - * @deprecated Use registerMemoryCapability({ promptBuilder }) instead. - */ - registerMemoryPromptSection: ( - builder: import("./memory-state.js").MemoryPromptSectionBuilder, - ) => void; - /** Register an additive memory-adjacent prompt section (non-exclusive). */ - registerMemoryPromptSupplement: ( - builder: import("./memory-state.js").MemoryPromptSectionBuilder, - ) => void; - /** Register an additive memory-adjacent search/read corpus supplement (non-exclusive). */ - registerMemoryCorpusSupplement: ( - supplement: import("./memory-state.js").MemoryCorpusSupplement, - ) => void; - /** - * Register the pre-compaction flush plan resolver for this memory plugin (exclusive slot). - * @deprecated Use registerMemoryCapability({ flushPlanResolver }) instead. - */ - registerMemoryFlushPlan: (resolver: import("./memory-state.js").MemoryFlushPlanResolver) => void; - /** - * Register the active memory runtime adapter for this memory plugin (exclusive slot). - * @deprecated Use registerMemoryCapability({ runtime }) instead. - */ - registerMemoryRuntime: (runtime: import("./memory-state.js").MemoryPluginRuntime) => void; - /** - * Register a memory embedding provider adapter. Multiple adapters may coexist. - * @deprecated New embedding providers should use `registerEmbeddingProvider` - * and `contracts.embeddingProviders`. This memory-specific seam is retained - * while existing memory providers migrate. - */ - registerMemoryEmbeddingProvider: ( - adapter: import("./memory-embedding-providers.js").MemoryEmbeddingProviderAdapter, - ) => void; - resolvePath: (input: string) => string; - /** Register a lifecycle hook handler */ - on: ( - hookName: K, - handler: PluginHookHandlerMap[K], - opts?: { priority?: number; timeoutMs?: number }, - ) => void; -}; - -// Plugin hook contracts now live in hook-types.ts so hook runners can import a -// leaf contract surface instead of pulling the full plugin runtime barrel. -/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */ +export type * from "./types.mcp-connection.js"; + +export { WorkerProviderError } from "./capability-provider.types.js"; +export type * from "./capability-provider.types.js"; +export type * from "./migration-provider.types.js"; +export type * from "./plugin-api.types.js"; +export { AGENT_PROMPT_SURFACE_KINDS } from "./plugin-command.types.js"; +export type * from "./plugin-command.types.js"; +export type * from "./plugin-config-schema.types.js"; +export type * from "./plugin-definition.types.js"; +export type * from "./plugin-registration.types.js"; +export type * from "./provider-authentication.types.js"; +export type * from "./provider-catalog.types.js"; +export type * from "./provider-plugin.types.js"; +export type * from "./provider-replay.types.js"; +export type * from "./provider-runtime.types.js"; +export type * from "./provider-transport.types.js";