refactor: move provider replay runtime ownership into plugins (#60126)

* refactor: move provider replay runtime ownership into plugins

* fix(provider-runtime): address review followups

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Josh Lehman
2026-04-03 07:14:37 -07:00
committed by GitHub
parent f328e7f4a6
commit 799c6f40aa
63 changed files with 2865 additions and 1802 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -12,10 +12,10 @@
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"index","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":233,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelConfigSchema = ChannelConfigSchema;","entrypoint":"index","exportName":"ChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":71,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelConfigUiHint = ChannelConfigUiHint;","entrypoint":"index","exportName":"ChannelConfigUiHint","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":40,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":675,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":680,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":696,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":275,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":672,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":677,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":693,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":272,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"index","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"index","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"index","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":524,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -28,7 +28,7 @@
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"index","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":102,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"ClawdbotConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type CliBackendConfig = CliBackendConfig;","entrypoint":"index","exportName":"CliBackendConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/config/types.agent-defaults.ts"}
{"declaration":"export type CliBackendPlugin = CliBackendPlugin;","entrypoint":"index","exportName":"CliBackendPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1844,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type CliBackendPlugin = CliBackendPlugin;","entrypoint":"index","exportName":"CliBackendPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1898,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type CompiledConfiguredBinding = CompiledConfiguredBinding;","entrypoint":"index","exportName":"CompiledConfiguredBinding","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":38,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type ConfiguredBindingConversation = ConversationRef;","entrypoint":"index","exportName":"ConfiguredBindingConversation","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type ConfiguredBindingResolution = ConfiguredBindingResolution;","entrypoint":"index","exportName":"ConfiguredBindingResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":49,"sourcePath":"src/channels/plugins/binding-types.ts"}
@@ -44,24 +44,24 @@
{"declaration":"export type ImageGenerationResolution = ImageGenerationResolution;","entrypoint":"index","exportName":"ImageGenerationResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type ImageGenerationResult = ImageGenerationResult;","entrypoint":"index","exportName":"ImageGenerationResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":36,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type ImageGenerationSourceImage = ImageGenerationSourceImage;","entrypoint":"index","exportName":"ImageGenerationSourceImage","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1476,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1530,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1888,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":105,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1942,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":104,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":75,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"index","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
{"declaration":"export type PluginRuntimeTaskFlows = PluginRuntimeTaskFlows;","entrypoint":"index","exportName":"PluginRuntimeTaskFlows","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":84,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type PluginRuntimeTaskRuns = PluginRuntimeTaskRuns;","entrypoint":"index","exportName":"PluginRuntimeTaskRuns","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":64,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type PluginRuntimeTasks = PluginRuntimeTasks;","entrypoint":"index","exportName":"PluginRuntimeTasks","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":94,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":180,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":165,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":321,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":179,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":164,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":320,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ReplyPayload = ReplyPayload;","entrypoint":"index","exportName":"ReplyPayload","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":85,"sourcePath":"src/auto-reply/types.ts"}
{"declaration":"export type RuntimeEnv = RuntimeEnv;","entrypoint":"index","exportName":"RuntimeEnv","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/runtime.ts"}
{"declaration":"export type RuntimeLogger = RuntimeLogger;","entrypoint":"index","exportName":"RuntimeLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/plugins/runtime/types-core.ts"}
{"declaration":"export type SecretInput = SecretInput;","entrypoint":"index","exportName":"SecretInput","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":16,"sourcePath":"src/config/types.secrets.ts"}
{"declaration":"export type SecretRef = SecretRef;","entrypoint":"index","exportName":"SecretRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":10,"sourcePath":"src/config/types.secrets.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1451,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1505,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type StatefulBindingTargetDescriptor = StatefulBindingTargetDescriptor;","entrypoint":"index","exportName":"StatefulBindingTargetDescriptor","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":17,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type StatefulBindingTargetDriver = StatefulBindingTargetDriver;","entrypoint":"index","exportName":"StatefulBindingTargetDriver","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
{"declaration":"export type StatefulBindingTargetReadyResult = StatefulBindingTargetReadyResult;","entrypoint":"index","exportName":"StatefulBindingTargetReadyResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
@@ -157,10 +157,10 @@
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-contract","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":609,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-contract","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":147,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-contract","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelApprovalAdapter = ChannelApprovalAdapter;","entrypoint":"channel-contract","exportName":"ChannelApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":613,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalCapability = ChannelApprovalCapability;","entrypoint":"channel-contract","exportName":"ChannelApprovalCapability","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":602,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandConversationContext = ChannelCommandConversationContext;","entrypoint":"channel-contract","exportName":"ChannelCommandConversationContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":684,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-contract","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":459,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalAdapter = ChannelApprovalAdapter;","entrypoint":"channel-contract","exportName":"ChannelApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":610,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalCapability = ChannelApprovalCapability;","entrypoint":"channel-contract","exportName":"ChannelApprovalCapability","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":599,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandConversationContext = ChannelCommandConversationContext;","entrypoint":"channel-contract","exportName":"ChannelCommandConversationContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":681,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-contract","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":456,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-contract","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":219,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"channel-contract","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"channel-contract","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":524,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -220,39 +220,39 @@
{"declaration":"export type ChannelAgentPromptAdapter = ChannelAgentPromptAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAgentPromptAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":497,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-runtime","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentToolFactory = ChannelAgentToolFactory;","entrypoint":"channel-runtime","exportName":"ChannelAgentToolFactory","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":24,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":619,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalAdapter = ChannelApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":613,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalCapability = ChannelApprovalCapability;","entrypoint":"channel-runtime","exportName":"ChannelApprovalCapability","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":602,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":616,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalAdapter = ChannelApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":610,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalCapability = ChannelApprovalCapability;","entrypoint":"channel-runtime","exportName":"ChannelApprovalCapability","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":599,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalForwardTarget = ChannelApprovalForwardTarget;","entrypoint":"channel-runtime","exportName":"ChannelApprovalForwardTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":39,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelApprovalInitiatingSurfaceState = ChannelActionAvailabilityState;","entrypoint":"channel-runtime","exportName":"ChannelApprovalInitiatingSurfaceState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":37,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":399,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":396,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"channel-runtime","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":233,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelCapabilitiesDiagnostics = ChannelCapabilitiesDiagnostics;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDiagnostics","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilitiesDisplayLine = ChannelCapabilitiesDisplayLine;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayLine","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":49,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilitiesDisplayTone = ChannelCapabilitiesDisplayTone;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayTone","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":497,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandConversationContext = ChannelCommandConversationContext;","entrypoint":"channel-runtime","exportName":"ChannelCommandConversationContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":684,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":494,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandConversationContext = ChannelCommandConversationContext;","entrypoint":"channel-runtime","exportName":"ChannelCommandConversationContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":681,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfigAdapter = ChannelConfigAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelConfigAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":98,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":675,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":680,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":696,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConversationBindingSupport = ChannelConversationBindingSupport;","entrypoint":"channel-runtime","exportName":"ChannelConversationBindingSupport","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":712,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":459,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":672,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":677,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":693,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConversationBindingSupport = ChannelConversationBindingSupport;","entrypoint":"channel-runtime","exportName":"ChannelConversationBindingSupport","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":709,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":456,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryEntry = ChannelDirectoryEntry;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntry","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":511,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelDirectoryEntryKind = ChannelDirectoryEntryKind;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntryKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":509,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":490,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":383,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":275,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupAdapter = ChannelGroupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGroupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":133,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":487,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":380,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":272,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupAdapter = ChannelGroupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGroupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":130,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-runtime","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":219,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":425,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":422,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelHeartbeatDeps = ChannelHeartbeatDeps;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatDeps","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":118,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"channel-runtime","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":502,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":354,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":359,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":364,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":348,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":499,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":351,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":356,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":361,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":345,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogSink = ChannelLogSink;","entrypoint":"channel-runtime","exportName":"ChannelLogSink","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":212,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMentionAdapter = ChannelMentionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMentionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":263,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -264,24 +264,24 @@
{"declaration":"export type ChannelMessageToolSchemaContribution = ChannelMessageToolSchemaContribution;","entrypoint":"channel-runtime","exportName":"ChannelMessageToolSchemaContribution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":52,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":398,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMeta = ChannelMeta;","entrypoint":"channel-runtime","exportName":"ChannelMeta","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":124,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":179,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundContext = ChannelOutboundContext;","entrypoint":"channel-runtime","exportName":"ChannelOutboundContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":139,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundPayloadHint = ChannelOutboundPayloadHint;","entrypoint":"channel-runtime","exportName":"ChannelOutboundPayloadHint","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":164,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":176,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundContext = ChannelOutboundContext;","entrypoint":"channel-runtime","exportName":"ChannelOutboundContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":136,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundPayloadHint = ChannelOutboundPayloadHint;","entrypoint":"channel-runtime","exportName":"ChannelOutboundPayloadHint","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":161,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundTargetMode = ChannelOutboundTargetMode;","entrypoint":"channel-runtime","exportName":"ChannelOutboundTargetMode","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":16,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundTargetRef = ChannelOutboundTargetRef;","entrypoint":"channel-runtime","exportName":"ChannelOutboundTargetRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":168,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":372,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundTargetRef = ChannelOutboundTargetRef;","entrypoint":"channel-runtime","exportName":"ChannelOutboundTargetRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":165,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":369,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":81,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelPollContext = ChannelPollContext;","entrypoint":"channel-runtime","exportName":"ChannelPollContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":591,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelPollResult = ChannelPollResult;","entrypoint":"channel-runtime","exportName":"ChannelPollResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":582,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":470,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":480,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":472,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":743,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":467,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":477,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":469,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":740,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityContext = ChannelSecurityContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":257,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelSecurityDmPolicy = ChannelSecurityDmPolicy;","entrypoint":"channel-runtime","exportName":"ChannelSecurityDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":248,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"channel-runtime","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":64,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":221,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":218,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"channel-runtime","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":102,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStreamingAdapter = ChannelStreamingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelStreamingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":282,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStructuredComponents = ChannelStructuredComponents;","entrypoint":"channel-runtime","exportName":"ChannelStructuredComponents","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":291,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -390,18 +390,18 @@
{"declaration":"export function applyAccountNameToChannelSection(params: { cfg: OpenClawConfig; channelKey: string; accountId: string; name?: string | undefined; alwaysUseAccounts?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"applyAccountNameToChannelSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":34,"sourcePath":"src/channels/plugins/setup-helpers.ts"}
{"declaration":"export function buildAgentSessionKey(params: { agentId: string; channel: string; accountId?: string | null | undefined; peer?: RoutePeer | null | undefined; dmScope?: \"main\" | \"per-peer\" | \"per-channel-peer\" | \"per-account-channel-peer\" | undefined; identityLinks?: Record<...> | undefined; }): string;","entrypoint":"core","exportName":"buildAgentSessionKey","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":92,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export function buildChannelConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, options?: BuildChannelConfigSchemaOptions | undefined): ChannelConfigSchema;","entrypoint":"core","exportName":"buildChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":76,"sourcePath":"src/channels/plugins/config-schema.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":206,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":194,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function buildPluginConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, options?: BuildPluginConfigSchemaOptions | undefined): OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"buildPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":78,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export function channelTargetSchema(options?: { description?: string | undefined; } | undefined): TString;","entrypoint":"core","exportName":"channelTargetSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function channelTargetsSchema(options?: { description?: string | undefined; } | undefined): TArray<TString>;","entrypoint":"core","exportName":"channelTargetsSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":44,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function clearAccountEntryFields<TAccountEntry extends object>(params: { accounts?: Record<string, TAccountEntry> | undefined; accountId: string; fields: string[]; isValueSet?: ((value: unknown) => boolean) | undefined; markClearedOnFieldPresence?: boolean | undefined; }): { ...; };","entrypoint":"core","exportName":"clearAccountEntryFields","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":563,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":536,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":551,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":524,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createDedupeCache(options: DedupeCacheOptions): DedupeCache;","entrypoint":"core","exportName":"createDedupeCache","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":17,"sourcePath":"src/infra/dedupe.ts"}
{"declaration":"export function createSubsystemLogger(subsystem: string): SubsystemLogger;","entrypoint":"core","exportName":"createSubsystemLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":308,"sourcePath":"src/logging/subsystem.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin>;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":300,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":151,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":343,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin>;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":288,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":159,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":331,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function delegateCompactionToRuntime(params: { sessionId: string; sessionKey?: string | undefined; sessionFile: string; tokenBudget?: number | undefined; force?: boolean | undefined; currentTokenCount?: number | undefined; compactionTarget?: \"budget\" | ... 1 more ... | undefined; customInstructions?: string | undefined; runtimeContext?: ContextEngineRuntimeContext | undefined; }): Promise<...>;","entrypoint":"core","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/context-engine/delegate.ts"}
{"declaration":"export function deleteAccountFromConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; clearBaseFields?: string[] | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"deleteAccountFromConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":60,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":108,"sourcePath":"src/plugins/config-schema.ts"}
@@ -426,148 +426,144 @@
{"declaration":"export function resolveThreadSessionKeys(params: { baseSessionKey: string; threadId?: string | null | undefined; parentSessionKey?: string | undefined; useSuffix?: boolean | undefined; normalizeThreadId?: ((threadId: string) => string) | undefined; }): { ...; };","entrypoint":"core","exportName":"resolveThreadSessionKeys","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":234,"sourcePath":"src/routing/session-key.ts"}
{"declaration":"export function setAccountEnabledInConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; enabled: boolean; allowTopLevel?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"setAccountEnabledInConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function stringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TUnsafe<T[number]>;","entrypoint":"core","exportName":"stringEnum","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":15,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":186,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":198,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":174,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":186,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function tryReadSecretFileSync(filePath: string | undefined, label: string, options?: SecretFileReadOptions): string | undefined;","entrypoint":"core","exportName":"tryReadSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":130,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export const DEFAULT_ACCOUNT_ID: \"default\";","entrypoint":"core","exportName":"DEFAULT_ACCOUNT_ID","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":3,"sourcePath":"src/routing/account-id.ts"}
{"declaration":"export const DEFAULT_SECRET_FILE_MAX_BYTES: number;","entrypoint":"core","exportName":"DEFAULT_SECRET_FILE_MAX_BYTES","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":5,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"core","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/agents/tools/common.ts"}
{"declaration":"export type BoundTaskFlowsRuntime = BoundTaskFlowsRuntime;","entrypoint":"core","exportName":"BoundTaskFlowsRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":74,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type BoundTaskRunsRuntime = BoundTaskRunsRuntime;","entrypoint":"core","exportName":"BoundTaskRunsRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type ChannelConfigUiHint = ChannelConfigUiHint;","entrypoint":"core","exportName":"ChannelConfigUiHint","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":40,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"core","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":524,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"core","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":398,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRoute = ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"ChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":312,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"../channels/plugins/types.core.js\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":181,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"../channels/plugins/types.core.js\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":169,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":81,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type GatewayBindUrlResult = GatewayBindUrlResult;","entrypoint":"core","exportName":"GatewayBindUrlResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/gateway-bind-url.ts"}
{"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":115,"sourcePath":"src/gateway/server-methods/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1476,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1530,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"core","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1888,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1599,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":105,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1870,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1837,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1829,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":120,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":145,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1491,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1635,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1942,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1653,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":104,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1924,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1891,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1883,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":119,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1545,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1689,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":75,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"core","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
{"declaration":"export type PluginRuntimeTaskFlows = PluginRuntimeTaskFlows;","entrypoint":"core","exportName":"PluginRuntimeTaskFlows","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":84,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type PluginRuntimeTaskRuns = PluginRuntimeTaskRuns;","entrypoint":"core","exportName":"PluginRuntimeTaskRuns","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":64,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type PluginRuntimeTasks = PluginRuntimeTasks;","entrypoint":"core","exportName":"PluginRuntimeTasks","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":94,"sourcePath":"src/plugins/runtime/runtime-tasks.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":801,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":180,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":519,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":259,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":243,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":165,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":713,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"core","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":729,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":745,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":754,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":701,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":280,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":303,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":778,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":817,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":500,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":788,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":364,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeToolSchemasContext = ProviderNormalizeToolSchemasContext;","entrypoint":"core","exportName":"ProviderNormalizeToolSchemasContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":617,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":446,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":355,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":533,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":425,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputMode = ProviderReasoningOutputMode;","entrypoint":"core","exportName":"ProviderReasoningOutputMode","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext;","entrypoint":"core","exportName":"ProviderReasoningOutputModeContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":627,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicy = ProviderReplayPolicy;","entrypoint":"core","exportName":"ProviderReplayPolicy","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":556,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicyContext = ProviderReplayPolicyContext;","entrypoint":"core","exportName":"ProviderReplayPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":577,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":487,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":338,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":468,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":321,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderSanitizeReplayHistoryContext = ProviderSanitizeReplayHistoryContext;","entrypoint":"core","exportName":"ProviderSanitizeReplayHistoryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":594,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":766,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":830,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":179,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":518,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":258,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":242,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":164,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":742,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"core","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":758,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":774,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":783,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":730,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":279,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":302,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":807,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":846,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":499,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":817,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":363,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeToolSchemasContext = ProviderNormalizeToolSchemasContext;","entrypoint":"core","exportName":"ProviderNormalizeToolSchemasContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":640,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":445,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":354,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":532,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":424,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputMode = ProviderReasoningOutputMode;","entrypoint":"core","exportName":"ProviderReasoningOutputMode","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":546,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext;","entrypoint":"core","exportName":"ProviderReasoningOutputModeContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":656,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicy = ProviderReplayPolicy;","entrypoint":"core","exportName":"ProviderReplayPolicy","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicyContext = ProviderReplayPolicyContext;","entrypoint":"core","exportName":"ProviderReplayPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":588,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplaySessionEntry = ProviderReplaySessionEntry;","entrypoint":"core","exportName":"ProviderReplaySessionEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":599,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplaySessionState = ProviderReplaySessionState;","entrypoint":"core","exportName":"ProviderReplaySessionState","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":604,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":486,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":337,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":467,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":320,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderSanitizeReplayHistoryContext = ProviderSanitizeReplayHistoryContext;","entrypoint":"core","exportName":"ProviderSanitizeReplayHistoryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":615,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":795,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderToolSchemaDiagnostic = ProviderToolSchemaDiagnostic;","entrypoint":"core","exportName":"ProviderToolSchemaDiagnostic","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":644,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderUsageSnapshot = ProviderUsageSnapshot;","entrypoint":"core","exportName":"ProviderUsageSnapshot","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export type ProviderValidateReplayTurnsContext = ProviderValidateReplayTurnsContext;","entrypoint":"core","exportName":"ProviderValidateReplayTurnsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":606,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":652,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderValidateReplayTurnsContext = ProviderValidateReplayTurnsContext;","entrypoint":"core","exportName":"ProviderValidateReplayTurnsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":628,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":681,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type RoutePeer = RoutePeer;","entrypoint":"core","exportName":"RoutePeer","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":21,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export type RoutePeerKind = ChatType;","entrypoint":"core","exportName":"RoutePeerKind","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export type SecretFileReadOptions = SecretFileReadOptions;","entrypoint":"core","exportName":"SecretFileReadOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export type SecretFileReadResult = SecretFileReadResult;","entrypoint":"core","exportName":"SecretFileReadResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1451,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1505,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type TailscaleStatusCommandResult = TailscaleStatusCommandResult;","entrypoint":"core","exportName":"TailscaleStatusCommandResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/shared/tailscale-status.ts"}
{"declaration":"export type TailscaleStatusCommandRunner = TailscaleStatusCommandRunner;","entrypoint":"core","exportName":"TailscaleStatusCommandRunner","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/shared/tailscale-status.ts"}
{"declaration":"export type TaskFlowDetail = TaskFlowDetail;","entrypoint":"core","exportName":"TaskFlowDetail","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":74,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type TaskFlowView = TaskFlowView;","entrypoint":"core","exportName":"TaskFlowView","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":60,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type TaskRunAggregateSummary = TaskRunAggregateSummary;","entrypoint":"core","exportName":"TaskRunAggregateSummary","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type TaskRunCancelResult = TaskRunCancelResult;","entrypoint":"core","exportName":"TaskRunCancelResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":53,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type TaskRunDetail = TaskRunView;","entrypoint":"core","exportName":"TaskRunDetail","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":51,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type TaskRunView = TaskRunView;","entrypoint":"core","exportName":"TaskRunView","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":23,"sourcePath":"src/plugins/runtime/task-domain-types.ts"}
{"declaration":"export type UsageProviderId = UsageProviderId;","entrypoint":"core","exportName":"UsageProviderId","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":20,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export type UsageWindow = UsageWindow;","entrypoint":"core","exportName":"UsageWindow","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export class KeyedAsyncQueue","entrypoint":"core","exportName":"KeyedAsyncQueue","importSpecifier":"openclaw/plugin-sdk/core","kind":"class","recordType":"export","sourceLine":34,"sourcePath":"src/plugin-sdk/keyed-async-queue.ts"}
{"category":"core","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":151,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":159,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":108,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"plugin-entry","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/agents/tools/common.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1476,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1530,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"plugin-entry","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1888,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1599,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":105,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1870,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1837,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1829,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":120,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":145,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1491,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1635,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":801,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":180,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":519,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":259,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":243,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":165,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":713,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":729,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":745,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":754,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":701,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":280,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":303,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":778,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":817,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":500,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":788,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeConfigContext = ProviderNormalizeConfigContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeConfigContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":390,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeModelIdContext = ProviderNormalizeModelIdContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeModelIdContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":379,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":364,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeToolSchemasContext = ProviderNormalizeToolSchemasContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeToolSchemasContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":617,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeTransportContext = ProviderNormalizeTransportContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeTransportContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":402,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":446,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":355,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":533,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":425,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputMode = ProviderReasoningOutputMode;","entrypoint":"plugin-entry","exportName":"ProviderReasoningOutputMode","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderReasoningOutputModeContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":627,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicy = ProviderReplayPolicy;","entrypoint":"plugin-entry","exportName":"ProviderReplayPolicy","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":556,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicyContext = ProviderReplayPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderReplayPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":577,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveConfigApiKeyContext = ProviderResolveConfigApiKeyContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveConfigApiKeyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":414,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":487,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":338,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":468,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":321,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderSanitizeReplayHistoryContext = ProviderSanitizeReplayHistoryContext;","entrypoint":"plugin-entry","exportName":"ProviderSanitizeReplayHistoryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":594,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":766,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderValidateReplayTurnsContext = ProviderValidateReplayTurnsContext;","entrypoint":"plugin-entry","exportName":"ProviderValidateReplayTurnsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":606,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":652,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1451,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1942,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1653,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":104,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1924,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1891,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1883,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":119,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1545,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1689,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":75,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":830,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":179,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":518,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":258,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":242,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":164,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":742,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":758,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":774,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":783,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":730,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":279,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":302,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":807,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDeferSyntheticProfileAuthContext = ProviderDeferSyntheticProfileAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderDeferSyntheticProfileAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":944,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":846,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":499,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":817,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeConfigContext = ProviderNormalizeConfigContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeConfigContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":389,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeModelIdContext = ProviderNormalizeModelIdContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeModelIdContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":378,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":363,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeToolSchemasContext = ProviderNormalizeToolSchemasContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeToolSchemasContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":640,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeTransportContext = ProviderNormalizeTransportContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeTransportContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":401,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":445,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":354,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":532,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":424,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputMode = ProviderReasoningOutputMode;","entrypoint":"plugin-entry","exportName":"ProviderReasoningOutputMode","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":546,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReasoningOutputModeContext = ProviderReplayPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderReasoningOutputModeContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":656,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicy = ProviderReplayPolicy;","entrypoint":"plugin-entry","exportName":"ProviderReplayPolicy","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplayPolicyContext = ProviderReplayPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderReplayPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":588,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplaySessionEntry = ProviderReplaySessionEntry;","entrypoint":"plugin-entry","exportName":"ProviderReplaySessionEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":599,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderReplaySessionState = ProviderReplaySessionState;","entrypoint":"plugin-entry","exportName":"ProviderReplaySessionState","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":604,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveConfigApiKeyContext = ProviderResolveConfigApiKeyContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveConfigApiKeyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":413,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":486,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":337,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":467,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":320,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderSanitizeReplayHistoryContext = ProviderSanitizeReplayHistoryContext;","entrypoint":"plugin-entry","exportName":"ProviderSanitizeReplayHistoryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":615,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":795,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderToolSchemaDiagnostic = ProviderToolSchemaDiagnostic;","entrypoint":"plugin-entry","exportName":"ProviderToolSchemaDiagnostic","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":644,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderValidateReplayTurnsContext = ProviderValidateReplayTurnsContext;","entrypoint":"plugin-entry","exportName":"ProviderValidateReplayTurnsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":628,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":681,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1505,"sourcePath":"src/plugins/types.ts"}
{"category":"provider","entrypoint":"provider-onboard","importSpecifier":"openclaw/plugin-sdk/provider-onboard","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
{"declaration":"export function applyAgentDefaultModelPrimary(cfg: OpenClawConfig, primary: string): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyAgentDefaultModelPrimary","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":271,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
{"declaration":"export function applyOnboardAuthAgentModelsAndProviders(cfg: OpenClawConfig, params: { agentModels: Record<string, AgentModelEntryConfig>; providers: Record<string, ModelProviderConfig>; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyOnboardAuthAgentModelsAndProviders","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":248,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
@@ -640,12 +636,12 @@
{"declaration":"export function createSandboxTestContext(params?: { overrides?: Partial<SandboxContext> | undefined; dockerOverrides?: Partial<SandboxDockerConfig> | undefined; } | undefined): SandboxContext;","entrypoint":"testing","exportName":"createSandboxTestContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":3,"sourcePath":"src/agents/sandbox/test-fixtures.ts"}
{"declaration":"export function createSlackOutboundPayloadHarness(params: { payload: ReplyPayload; sendResults?: { messageId: string; }[] | undefined; }): SlackOutboundPayloadHarness;","entrypoint":"testing","exportName":"createSlackOutboundPayloadHarness","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":126,"sourcePath":"src/channels/plugins/contracts/suites.ts"}
{"declaration":"export function createWhatsAppPollFixture(): { cfg: OpenClawConfig; poll: { question: string; options: string[]; maxSelections: number; }; to: string; accountId: string; };","entrypoint":"testing","exportName":"createWhatsAppPollFixture","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":4,"sourcePath":"src/test-helpers/whatsapp-outbound.ts"}
{"declaration":"export function createWindowsCmdShimFixture(params: { shimPath: string; scriptPath: string; shimLine: string; }): Promise<void>;","entrypoint":"testing","exportName":"createWindowsCmdShimFixture","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":62,"sourcePath":"src/plugin-sdk/testing.ts"}
{"declaration":"export function createWindowsCmdShimFixture(params: { shimPath: string; scriptPath: string; shimLine: string; }): Promise<void>;","entrypoint":"testing","exportName":"createWindowsCmdShimFixture","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":4,"sourcePath":"src/test-helpers/windows-cmd-shim.ts"}
{"declaration":"export function expectChannelInboundContextContract(ctx: MsgContext): void;","entrypoint":"testing","exportName":"expectChannelInboundContextContract","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":856,"sourcePath":"src/channels/plugins/contracts/suites.ts"}
{"declaration":"export function expectWhatsAppPollSent(sendPollWhatsApp: MockInstance<Procedure>, params: { cfg: OpenClawConfig; poll: { question: string; options: string[]; maxSelections: number; }; to?: string | undefined; accountId?: string | undefined; }): void;","entrypoint":"testing","exportName":"expectWhatsAppPollSent","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":19,"sourcePath":"src/test-helpers/whatsapp-outbound.ts"}
{"declaration":"export function firstWrittenJsonArg<T>(writeJson: MockCallsWithFirstArg): T | null;","entrypoint":"testing","exportName":"firstWrittenJsonArg","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":92,"sourcePath":"src/cli/test-runtime-capture.ts"}
{"declaration":"export function getActivePluginRegistry(): PluginRegistry | null;","entrypoint":"testing","exportName":"getActivePluginRegistry","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":95,"sourcePath":"src/plugins/runtime.ts"}
{"declaration":"export function installCommonResolveTargetErrorCases(params: { resolveTarget: ResolveTargetFn; implicitAllowFrom: string[]; }): void;","entrypoint":"testing","exportName":"installCommonResolveTargetErrorCases","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":88,"sourcePath":"src/plugin-sdk/testing.ts"}
{"declaration":"export function installCommonResolveTargetErrorCases(params: { resolveTarget: ResolveTargetFn; implicitAllowFrom: string[]; }): void;","entrypoint":"testing","exportName":"installCommonResolveTargetErrorCases","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":17,"sourcePath":"src/test-helpers/resolve-target-error-cases.ts"}
{"declaration":"export function installPinnedHostnameTestHooks(): void;","entrypoint":"testing","exportName":"installPinnedHostnameTestHooks","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/media-understanding/audio.test-helpers.ts"}
{"declaration":"export function isLiveTestEnabled(extraEnvVars?: readonly string[], env?: ProcessEnv): boolean;","entrypoint":"testing","exportName":"isLiveTestEnabled","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":5,"sourcePath":"src/agents/live-test-helpers.ts"}
{"declaration":"export function jsonResponse(body: unknown, status?: number): Response;","entrypoint":"testing","exportName":"jsonResponse","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":1,"sourcePath":"src/test-helpers/http.ts"}
@@ -672,7 +668,7 @@
{"declaration":"export const __testing: { resetAcpSessionManagerForTests(): void; setAcpSessionManagerForTests(manager: unknown): void; };","entrypoint":"testing","exportName":"acpManagerTesting","importSpecifier":"openclaw/plugin-sdk/testing","kind":"const","recordType":"export","sourceLine":25,"sourcePath":"src/acp/control-plane/manager.ts"}
{"declaration":"export const handleAcpCommand: CommandHandler;","entrypoint":"testing","exportName":"handleAcpCommand","importSpecifier":"openclaw/plugin-sdk/testing","kind":"const","recordType":"export","sourceLine":75,"sourcePath":"src/auto-reply/reply/commands-acp.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"testing","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":147,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":275,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":272,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type CliMockOutputRuntime = CliMockOutputRuntime;","entrypoint":"testing","exportName":"CliMockOutputRuntime","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":5,"sourcePath":"src/cli/test-runtime-capture.ts"}
{"declaration":"export type CliRuntimeCapture = CliRuntimeCapture;","entrypoint":"testing","exportName":"CliRuntimeCapture","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/cli/test-runtime-capture.ts"}
{"declaration":"export type MockFn = MockFn<T>;","entrypoint":"testing","exportName":"MockFn","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":5,"sourcePath":"src/test-utils/vitest-mock-fn.ts"}

View File

@@ -502,9 +502,7 @@ if (sandboxRoot) {
### Google/Gemini
- Turn ordering fixes (`applyGoogleTurnOrderingFix`)
- Tool schema sanitization (`sanitizeToolsForGoogle`)
- Session history sanitization (`sanitizeSessionHistory`)
- Plugin-owned tool schema sanitization
### OpenAI

View File

@@ -105,6 +105,27 @@ describe("amazon-bedrock provider plugin", () => {
).toBeUndefined();
});
it("owns Anthropic-style replay policy for Claude Bedrock models", () => {
const provider = registerSingleProviderPlugin(amazonBedrockPlugin);
expect(
provider.buildReplayPolicy?.({
provider: "amazon-bedrock",
modelApi: "bedrock-converse-stream",
modelId: ANTHROPIC_MODEL,
} as never),
).toEqual({
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
dropThinkingBlocks: true,
});
});
it("disables prompt caching for non-Anthropic Bedrock models", () => {
const provider = registerSingleProviderPlugin(amazonBedrockPlugin);
const wrapped = provider.wrapStreamFn?.({

View File

@@ -46,6 +46,19 @@ function createGuardrailWrapStreamFn(
const PROVIDER_ID = "amazon-bedrock";
const CLAUDE_46_MODEL_RE = /claude-(?:opus|sonnet)-4(?:\.|-)6(?:$|[-.])/i;
function buildAmazonBedrockReplayPolicy(modelId?: string) {
return {
sanitizeMode: "full" as const,
sanitizeToolCallIds: true,
toolCallIdMode: "strict" as const,
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
...((modelId?.toLowerCase() ?? "").includes("claude") ? { dropThinkingBlocks: true } : {}),
};
}
export default definePluginEntry({
id: PROVIDER_ID,
name: "Amazon Bedrock Provider",
@@ -87,10 +100,7 @@ export default definePluginEntry({
},
},
resolveConfigApiKey: ({ env }) => resolveBedrockConfigApiKey(env),
capabilities: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
buildReplayPolicy: ({ modelId }) => buildAmazonBedrockReplayPolicy(modelId),
wrapStreamFn,
resolveDefaultThinkingLevel: ({ modelId }) =>
CLAUDE_46_MODEL_RE.test(modelId.trim()) ? "adaptive" : undefined,

View File

@@ -56,4 +56,25 @@ describe("anthropic-vertex provider plugin", () => {
},
});
});
it("owns Anthropic-style replay policy", () => {
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
expect(
provider.buildReplayPolicy?.({
provider: "anthropic-vertex",
modelApi: "anthropic-messages",
modelId: "claude-sonnet-4-6",
} as never),
).toEqual({
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
dropThinkingBlocks: true,
});
});
});

View File

@@ -7,6 +7,19 @@ import {
const PROVIDER_ID = "anthropic-vertex";
function buildAnthropicVertexReplayPolicy(modelId?: string) {
return {
sanitizeMode: "full" as const,
sanitizeToolCallIds: true,
toolCallIdMode: "strict" as const,
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
...((modelId?.toLowerCase() ?? "").includes("claude") ? { dropThinkingBlocks: true } : {}),
};
}
export default definePluginEntry({
id: PROVIDER_ID,
name: "Anthropic Vertex Provider",
@@ -35,10 +48,7 @@ export default definePluginEntry({
},
},
resolveConfigApiKey: ({ env }) => resolveAnthropicVertexConfigApiKey(env),
capabilities: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
buildReplayPolicy: ({ modelId }) => buildAnthropicVertexReplayPolicy(modelId),
});
},
});

View File

@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import anthropicPlugin from "./index.js";
describe("anthropic provider replay hooks", () => {
it("owns replay policy for Claude transports", () => {
const provider = registerSingleProviderPlugin(anthropicPlugin);
expect(
provider.buildReplayPolicy?.({
provider: "anthropic",
modelApi: "anthropic-messages",
modelId: "claude-sonnet-4-6",
} as never),
).toEqual({
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
dropThinkingBlocks: true,
});
});
});

View File

@@ -30,6 +30,7 @@ import { fetchClaudeUsage } from "openclaw/plugin-sdk/provider-usage";
import { buildAnthropicCliBackend } from "./cli-backend.js";
import { buildAnthropicCliMigrationResult, hasClaudeCliAuth } from "./cli-migration.js";
import { anthropicMediaUnderstandingProvider } from "./media-understanding-provider.js";
import { buildAnthropicReplayPolicy } from "./replay-policy.js";
import {
createAnthropicBetaHeadersWrapper,
createAnthropicFastModeWrapper,
@@ -446,10 +447,7 @@ export default definePluginEntry({
}),
],
resolveDynamicModel: (ctx) => resolveAnthropicForwardCompatModel(ctx),
capabilities: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
buildReplayPolicy: (ctx) => buildAnthropicReplayPolicy(ctx),
isModernModelRef: ({ modelId }) => matchesAnthropicModernModel(modelId),
wrapStreamFn: (ctx) => {
let streamFn = ctx.streamFn;

View File

@@ -0,0 +1,22 @@
import type {
ProviderReplayPolicy,
ProviderReplayPolicyContext,
} from "openclaw/plugin-sdk/plugin-entry";
/**
* Returns the provider-owned replay policy for Anthropic transports.
*/
export function buildAnthropicReplayPolicy(ctx: ProviderReplayPolicyContext): ProviderReplayPolicy {
const modelId = ctx.modelId?.toLowerCase() ?? "";
return {
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
...(modelId.includes("claude") ? { dropThinkingBlocks: true } : {}),
};
}

View File

@@ -12,6 +12,14 @@ import { fetchCopilotUsage } from "./usage.js";
const COPILOT_ENV_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"];
const COPILOT_XHIGH_MODEL_IDS = ["gpt-5.2", "gpt-5.2-codex"] as const;
function buildGithubCopilotReplayPolicy(modelId?: string) {
return (modelId?.toLowerCase() ?? "").includes("claude")
? {
dropThinkingBlocks: true,
}
: {};
}
function resolveFirstGithubToken(params: { agentDir?: string; env: NodeJS.ProcessEnv }): {
githubToken: string;
hasProfile: boolean;
@@ -144,9 +152,7 @@ export default definePluginEntry({
},
},
resolveDynamicModel: (ctx) => resolveCopilotForwardCompatModel(ctx),
capabilities: {
dropThinkingBlockModelHints: ["claude"],
},
buildReplayPolicy: ({ modelId }) => buildGithubCopilotReplayPolicy(modelId),
supportsXHighThinking: ({ modelId }) =>
COPILOT_XHIGH_MODEL_IDS.includes(modelId.trim().toLowerCase() as never),
prepareRuntimeAuth: async (ctx) => {

View File

@@ -4,8 +4,16 @@ import type {
ProviderFetchUsageSnapshotContext,
} from "openclaw/plugin-sdk/plugin-entry";
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth-result";
import { createGoogleThinkingPayloadWrapper } from "openclaw/plugin-sdk/provider-stream";
import { fetchGeminiUsage } from "openclaw/plugin-sdk/provider-usage";
import { isModernGoogleModel, resolveGoogle31ForwardCompatModel } from "./provider-models.js";
import {
buildGoogleReplayPolicy,
inspectGoogleGeminiCliToolSchemas,
normalizeGoogleGeminiCliToolSchemas,
resolveGoogleReasoningOutputMode,
sanitizeGoogleReplayHistory,
} from "./replay-policy.js";
const PROVIDER_ID = "google-gemini-cli";
const PROVIDER_LABEL = "Gemini CLI OAuth";
@@ -123,6 +131,12 @@ export function registerGoogleGeminiCliProvider(api: OpenClawPluginApi) {
},
resolveDynamicModel: (ctx) =>
resolveGoogle31ForwardCompatModel({ providerId: PROVIDER_ID, ctx }),
buildReplayPolicy: () => buildGoogleReplayPolicy(),
wrapStreamFn: (ctx) => createGoogleThinkingPayloadWrapper(ctx.streamFn, ctx.thinkingLevel),
sanitizeReplayHistory: (ctx) => sanitizeGoogleReplayHistory(ctx),
normalizeToolSchemas: (ctx) => normalizeGoogleGeminiCliToolSchemas(ctx),
inspectToolSchemas: (ctx) => inspectGoogleGeminiCliToolSchemas(ctx),
resolveReasoningOutputMode: () => resolveGoogleReasoningOutputMode(),
isModernModelRef: ({ modelId }) => isModernGoogleModel(modelId),
formatApiKey: (cred) => formatGoogleOauthApiKey(cred),
resolveUsageAuth: async (ctx) => {

View File

@@ -0,0 +1,130 @@
import type {
ProviderReplaySessionEntry,
ProviderSanitizeReplayHistoryContext,
} from "openclaw/plugin-sdk/plugin-entry";
import { describe, expect, it } from "vitest";
import {
registerProviderPlugin,
requireRegisteredProvider,
} from "../../test/helpers/plugins/provider-registration.js";
import googlePlugin from "./index.js";
describe("google provider plugin hooks", () => {
it("owns replay policy and reasoning mode for the direct Gemini provider", async () => {
const { providers } = registerProviderPlugin({
plugin: googlePlugin,
id: "google",
name: "Google Provider",
});
const provider = requireRegisteredProvider(providers, "google");
const customEntries: ProviderReplaySessionEntry[] = [];
expect(
provider.buildReplayPolicy?.({
provider: "google",
modelApi: "google-generative-ai",
modelId: "gemini-3.1-pro-preview",
} as never),
).toEqual({
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
repairToolUseResultPairing: true,
applyAssistantFirstOrderingFix: true,
validateGeminiTurns: true,
validateAnthropicTurns: false,
allowSyntheticToolResults: true,
});
expect(
provider.resolveReasoningOutputMode?.({
provider: "google",
modelApi: "google-generative-ai",
modelId: "gemini-3.1-pro-preview",
} as never),
).toBe("tagged");
const sanitized = await Promise.resolve(
provider.sanitizeReplayHistory?.({
provider: "google",
modelApi: "google-generative-ai",
modelId: "gemini-3.1-pro-preview",
sessionId: "session-1",
messages: [
{
role: "assistant",
content: [{ type: "text", text: "hello" }],
},
],
sessionState: {
getCustomEntries: () => customEntries,
appendCustomEntry: (customType: string, data: unknown) => {
customEntries.push({ customType, data });
},
},
} as ProviderSanitizeReplayHistoryContext),
);
expect(sanitized).toEqual(
expect.arrayContaining([
expect.objectContaining({
role: "user",
content: "(session bootstrap)",
}),
]),
);
expect(customEntries).toHaveLength(1);
expect(customEntries[0]?.customType).toBe("google-turn-ordering-bootstrap");
});
it("owns Gemini CLI tool schema normalization", () => {
const { providers } = registerProviderPlugin({
plugin: googlePlugin,
id: "google",
name: "Google Provider",
});
const provider = requireRegisteredProvider(providers, "google-gemini-cli");
const [tool] =
provider.normalizeToolSchemas?.({
provider: "google-gemini-cli",
tools: [
{
name: "write_file",
description: "Write a file",
parameters: {
type: "object",
additionalProperties: false,
properties: {
path: { type: "string", pattern: "^src/" },
},
},
},
],
} as never) ?? [];
expect(tool).toMatchObject({
name: "write_file",
parameters: {
type: "object",
properties: {
path: { type: "string" },
},
},
});
expect(tool?.parameters).not.toHaveProperty("additionalProperties");
expect(
(tool?.parameters as { properties?: { path?: Record<string, unknown> } })?.properties?.path,
).not.toHaveProperty("pattern");
expect(
provider.inspectToolSchemas?.({
provider: "google-gemini-cli",
tools: [tool],
} as never),
).toEqual([]);
});
});

View File

@@ -18,6 +18,13 @@ import {
} from "./api.js";
import { buildGoogleGeminiCliBackend } from "./cli-backend.js";
import { isModernGoogleModel, resolveGoogle31ForwardCompatModel } from "./provider-models.js";
import {
buildGoogleReplayPolicy,
inspectGoogleGeminiCliToolSchemas,
normalizeGoogleGeminiCliToolSchemas,
resolveGoogleReasoningOutputMode,
sanitizeGoogleReplayHistory,
} from "./replay-policy.js";
import { createGeminiWebSearchProvider } from "./src/gemini-web-search-provider.js";
const GOOGLE_GEMINI_CLI_PROVIDER_ID = "google-gemini-cli";
@@ -140,6 +147,12 @@ function createLazyGoogleGeminiCliProvider(): ProviderPlugin {
normalizeModelId: ({ modelId }) => normalizeGoogleModelId(modelId),
resolveDynamicModel: (ctx) =>
resolveGoogle31ForwardCompatModel({ providerId: GOOGLE_GEMINI_CLI_PROVIDER_ID, ctx }),
buildReplayPolicy: () => buildGoogleReplayPolicy(),
wrapStreamFn: (ctx) => createGoogleThinkingPayloadWrapper(ctx.streamFn, ctx.thinkingLevel),
sanitizeReplayHistory: (ctx) => sanitizeGoogleReplayHistory(ctx),
normalizeToolSchemas: (ctx) => normalizeGoogleGeminiCliToolSchemas(ctx),
inspectToolSchemas: (ctx) => inspectGoogleGeminiCliToolSchemas(ctx),
resolveReasoningOutputMode: () => resolveGoogleReasoningOutputMode(),
isModernModelRef: ({ modelId }) => isModernGoogleModel(modelId),
formatApiKey: (cred) => formatGoogleOauthApiKey(cred as GoogleOauthApiKeyCredential),
resolveUsageAuth: async (ctx) => {
@@ -247,6 +260,9 @@ export default definePluginEntry({
ctx,
}),
wrapStreamFn: (ctx) => createGoogleThinkingPayloadWrapper(ctx.streamFn, ctx.thinkingLevel),
buildReplayPolicy: () => buildGoogleReplayPolicy(),
sanitizeReplayHistory: (ctx) => sanitizeGoogleReplayHistory(ctx),
resolveReasoningOutputMode: () => resolveGoogleReasoningOutputMode(),
isModernModelRef: ({ modelId }) => isModernGoogleModel(modelId),
});
api.registerCliBackend(buildGoogleGeminiCliBackend());

View File

@@ -0,0 +1,135 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type {
AnyAgentTool,
ProviderNormalizeToolSchemasContext,
ProviderReasoningOutputMode,
ProviderReplayPolicy,
ProviderReplaySessionState,
ProviderSanitizeReplayHistoryContext,
ProviderToolSchemaDiagnostic,
} from "openclaw/plugin-sdk/plugin-entry";
import {
cleanSchemaForGemini,
findUnsupportedSchemaKeywords,
GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS,
} from "openclaw/plugin-sdk/provider-tools";
const GOOGLE_TURN_ORDERING_CUSTOM_TYPE = "google-turn-ordering-bootstrap";
const GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT = "(session bootstrap)";
function sanitizeGoogleAssistantFirstOrdering(messages: AgentMessage[]): AgentMessage[] {
const first = messages[0] as { role?: unknown; content?: unknown } | undefined;
const role = first?.role;
const content = first?.content;
if (
role === "user" &&
typeof content === "string" &&
content.trim() === GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT
) {
return messages;
}
if (role !== "assistant") {
return messages;
}
const bootstrap: AgentMessage = {
role: "user",
content: GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT,
timestamp: Date.now(),
} as AgentMessage;
return [bootstrap, ...messages];
}
function hasGoogleTurnOrderingMarker(sessionState: ProviderReplaySessionState): boolean {
return sessionState
.getCustomEntries()
.some((entry) => entry.customType === GOOGLE_TURN_ORDERING_CUSTOM_TYPE);
}
function markGoogleTurnOrderingMarker(sessionState: ProviderReplaySessionState): void {
sessionState.appendCustomEntry(GOOGLE_TURN_ORDERING_CUSTOM_TYPE, {
timestamp: Date.now(),
});
}
/**
* Returns the provider-owned replay policy for Google Gemini transports.
*/
export function buildGoogleReplayPolicy(): ProviderReplayPolicy {
return {
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
repairToolUseResultPairing: true,
applyAssistantFirstOrderingFix: true,
validateGeminiTurns: true,
validateAnthropicTurns: false,
allowSyntheticToolResults: true,
};
}
/**
* Returns the provider-owned reasoning output mode for Google Gemini transports.
*/
export function resolveGoogleReasoningOutputMode(): ProviderReasoningOutputMode {
return "tagged";
}
/**
* Applies the provider-owned replay ordering fix for Gemini transports.
*/
export function sanitizeGoogleReplayHistory(
ctx: ProviderSanitizeReplayHistoryContext,
): AgentMessage[] {
const messages = sanitizeGoogleAssistantFirstOrdering(ctx.messages);
if (
messages !== ctx.messages &&
ctx.sessionState &&
!hasGoogleTurnOrderingMarker(ctx.sessionState)
) {
markGoogleTurnOrderingMarker(ctx.sessionState);
}
return messages;
}
/**
* Normalizes Gemini CLI tool schemas to the restricted JSON Schema subset
* accepted by the Cloud Code Assist transport.
*/
export function normalizeGoogleGeminiCliToolSchemas(
ctx: ProviderNormalizeToolSchemasContext,
): AnyAgentTool[] {
return ctx.tools.map((tool) => {
if (!tool.parameters || typeof tool.parameters !== "object") {
return tool;
}
return {
...tool,
parameters: cleanSchemaForGemini(tool.parameters as Record<string, unknown>),
};
});
}
/**
* Reports any remaining Gemini CLI schema violations after normalization.
*/
export function inspectGoogleGeminiCliToolSchemas(
ctx: ProviderNormalizeToolSchemasContext,
): ProviderToolSchemaDiagnostic[] {
return ctx.tools.flatMap((tool, toolIndex) => {
const violations = findUnsupportedSchemaKeywords(
tool.parameters,
`${tool.name}.parameters`,
GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS,
);
if (violations.length === 0) {
return [];
}
return [{ toolName: tool.name, toolIndex, violations }];
});
}

View File

@@ -8,6 +8,19 @@ import { buildKilocodeProviderWithDiscovery } from "./provider-catalog.js";
const PROVIDER_ID = "kilocode";
function buildKilocodeReplayPolicy(modelId?: string) {
const normalizedModelId = modelId?.toLowerCase() ?? "";
if (!normalizedModelId.includes("gemini")) {
return {};
}
return {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
};
}
export default defineSingleProviderPluginEntry({
id: PROVIDER_ID,
name: "Kilo Gateway Provider",
@@ -31,10 +44,7 @@ export default defineSingleProviderPluginEntry({
catalog: {
buildProvider: buildKilocodeProviderWithDiscovery,
},
capabilities: {
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
buildReplayPolicy: ({ modelId }) => buildKilocodeReplayPolicy(modelId),
wrapStreamFn: (ctx) => {
const thinkingLevel =
ctx.modelId === "kilo/auto" || isProxyReasoningUnsupported(ctx.modelId)

View File

@@ -2,7 +2,7 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { applyKimiCodeConfig, KIMI_CODING_MODEL_REF } from "./onboard.js";
import { buildKimiCodingProvider } from "./provider-catalog.js";
import { createKimiToolCallMarkupWrapper } from "./stream.js";
import { wrapKimiProviderStream } from "./stream.js";
const PLUGIN_ID = "kimi";
const PROVIDER_ID = "kimi";
@@ -11,6 +11,12 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function buildKimiReplayPolicy() {
return {
preserveSignatures: false,
};
}
export default definePluginEntry({
id: PLUGIN_ID,
name: "Kimi Provider",
@@ -80,13 +86,8 @@ export default definePluginEntry({
};
},
},
capabilities: {
anthropicToolSchemaMode: "openai-functions",
anthropicToolChoiceMode: "openai-string-modes",
openAiPayloadNormalizationMode: "moonshot-thinking",
preserveAnthropicThinkingSignatures: false,
},
wrapStreamFn: (ctx) => createKimiToolCallMarkupWrapper(ctx.streamFn),
buildReplayPolicy: () => buildKimiReplayPolicy(),
wrapStreamFn: (ctx) => wrapKimiProviderStream(ctx.streamFn),
});
},
});

View File

@@ -1,5 +1,6 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import { streamSimple } from "@mariozechner/pi-ai";
import { createAnthropicToolPayloadCompatibilityWrapper } from "openclaw/plugin-sdk/provider-stream";
const TOOL_CALLS_SECTION_BEGIN = "<|tool_calls_section_begin|>";
const TOOL_CALLS_SECTION_END = "<|tool_calls_section_end|>";
@@ -179,3 +180,12 @@ export function createKimiToolCallMarkupWrapper(baseStreamFn: StreamFn | undefin
return wrapKimiTaggedToolCalls(maybeStream);
};
}
export function wrapKimiProviderStream(baseStreamFn: StreamFn | undefined): StreamFn {
return createKimiToolCallMarkupWrapper(
createAnthropicToolPayloadCompatibilityWrapper(baseStreamFn, {
toolSchemaMode: "openai-functions",
toolChoiceMode: "openai-string-modes",
}),
);
}

View File

@@ -26,9 +26,6 @@ export function buildMicrosoftFoundryProvider(): ProviderPlugin {
docsPath: "/providers/models",
envVars: ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
auth: [entraIdAuthMethod, apiKeyAuthMethod],
capabilities: {
providerFamily: "openai" as const,
},
onModelSelected: async (ctx) => {
const providerConfig = ctx.config.models?.providers?.[PROVIDER_ID];
if (!providerConfig || !ctx.model.startsWith(`${PROVIDER_ID}/`)) {

View File

@@ -0,0 +1,95 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import type { Context, Model } from "@mariozechner/pi-ai";
import { describe, expect, it } from "vitest";
import {
registerProviderPlugin,
requireRegisteredProvider,
} from "../../test/helpers/plugins/provider-registration.js";
import minimaxPlugin from "./index.js";
describe("minimax provider hooks", () => {
it("keeps native reasoning mode for MiniMax transports", () => {
const { providers } = registerProviderPlugin({
plugin: minimaxPlugin,
id: "minimax",
name: "MiniMax Provider",
});
const apiProvider = requireRegisteredProvider(providers, "minimax");
const portalProvider = requireRegisteredProvider(providers, "minimax-portal");
expect(apiProvider.hookAliases).toContain("minimax-cn");
expect(
apiProvider.resolveReasoningOutputMode?.({
provider: "minimax",
modelApi: "anthropic-messages",
modelId: "MiniMax-M2.7",
} as never),
).toBe("native");
expect(portalProvider.hookAliases).toContain("minimax-portal-cn");
expect(
portalProvider.resolveReasoningOutputMode?.({
provider: "minimax-portal",
modelApi: "anthropic-messages",
modelId: "MiniMax-M2.7",
} as never),
).toBe("native");
});
it("owns fast-mode stream wrapping for MiniMax transports", () => {
const { providers } = registerProviderPlugin({
plugin: minimaxPlugin,
id: "minimax",
name: "MiniMax Provider",
});
const apiProvider = requireRegisteredProvider(providers, "minimax");
const portalProvider = requireRegisteredProvider(providers, "minimax-portal");
let resolvedApiModelId = "";
const captureApiModel: StreamFn = (model) => {
resolvedApiModelId = String(model.id ?? "");
return {} as ReturnType<StreamFn>;
};
const wrappedApiStream = apiProvider.wrapStreamFn?.({
provider: "minimax",
modelId: "MiniMax-M2.7",
extraParams: { fastMode: true },
streamFn: captureApiModel,
} as never);
void wrappedApiStream?.(
{
api: "anthropic-messages",
provider: "minimax",
id: "MiniMax-M2.7",
} as Model<"anthropic-messages">,
{ messages: [] } as Context,
{},
);
let resolvedPortalModelId = "";
const capturePortalModel: StreamFn = (model) => {
resolvedPortalModelId = String(model.id ?? "");
return {} as ReturnType<StreamFn>;
};
const wrappedPortalStream = portalProvider.wrapStreamFn?.({
provider: "minimax-portal",
modelId: "MiniMax-M2.7",
extraParams: { fastMode: true },
streamFn: capturePortalModel,
} as never);
void wrappedPortalStream?.(
{
api: "anthropic-messages",
provider: "minimax-portal",
id: "MiniMax-M2.7",
} as Model<"anthropic-messages">,
{ messages: [] } as Context,
{},
);
expect(resolvedApiModelId).toBe("MiniMax-M2.7-highspeed");
expect(resolvedPortalModelId).toBe("MiniMax-M2.7-highspeed");
});
});

View File

@@ -11,6 +11,7 @@ import {
} from "openclaw/plugin-sdk/provider-auth";
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { createMinimaxFastModeWrapper } from "openclaw/plugin-sdk/provider-stream";
import { fetchMinimaxUsage } from "openclaw/plugin-sdk/provider-usage";
import { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID } from "./api.js";
import {
@@ -32,6 +33,12 @@ const DEFAULT_MODEL = MINIMAX_DEFAULT_MODEL_ID;
const DEFAULT_BASE_URL_CN = "https://api.minimaxi.com/anthropic";
const DEFAULT_BASE_URL_GLOBAL = "https://api.minimax.io/anthropic";
function resolveMinimaxReasoningOutputMode(): "native" {
// Keep MiniMax on native reasoning mode. Tagged enforcement previously
// suppressed normal assistant replies on this Anthropic-compatible surface.
return "native";
}
function getDefaultBaseUrl(region: MiniMaxRegion): string {
return region === "cn" ? DEFAULT_BASE_URL_CN : DEFAULT_BASE_URL_GLOBAL;
}
@@ -165,10 +172,9 @@ export default definePluginEntry({
api.registerProvider({
id: API_PROVIDER_ID,
label: PROVIDER_LABEL,
hookAliases: ["minimax-cn"],
docsPath: "/providers/minimax",
aliases: ["minimax-cn"],
envVars: ["MINIMAX_API_KEY"],
resolveReasoningOutputMode: () => "native",
auth: [
createProviderApiKeyAuthMethod({
providerId: API_PROVIDER_ID,
@@ -229,6 +235,9 @@ export default definePluginEntry({
});
return apiKey ? { token: apiKey } : null;
},
wrapStreamFn: (ctx) =>
createMinimaxFastModeWrapper(ctx.streamFn, ctx.extraParams?.fastMode === true),
resolveReasoningOutputMode: () => resolveMinimaxReasoningOutputMode(),
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
fetchUsageSnapshot: async (ctx) =>
await fetchMinimaxUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
@@ -240,9 +249,9 @@ export default definePluginEntry({
api.registerProvider({
id: PORTAL_PROVIDER_ID,
label: PROVIDER_LABEL,
hookAliases: ["minimax-portal-cn"],
docsPath: "/providers/minimax",
envVars: ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"],
resolveReasoningOutputMode: () => "native",
catalog: {
run: async (ctx) => resolvePortalCatalog(ctx),
},
@@ -278,6 +287,9 @@ export default definePluginEntry({
run: createOAuthHandler("cn"),
},
],
wrapStreamFn: (ctx) =>
createMinimaxFastModeWrapper(ctx.streamFn, ctx.extraParams?.fastMode === true),
resolveReasoningOutputMode: () => resolveMinimaxReasoningOutputMode(),
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
});
api.registerImageGenerationProvider(buildMinimaxImageGenerationProvider());

View File

@@ -47,6 +47,13 @@ function shouldContributeMistralCompat(params: {
return isMistralBaseUrl(params.model.baseUrl) || isMistralModelHint(params.modelId);
}
function buildMistralReplayPolicy() {
return {
sanitizeToolCallIds: true,
toolCallIdMode: "strict9" as const,
};
}
export default defineSingleProviderPluginEntry({
id: PROVIDER_ID,
name: "Mistral Provider",
@@ -77,18 +84,7 @@ export default defineSingleProviderPluginEntry({
normalizeResolvedModel: ({ model }) => applyMistralModelCompat(model),
contributeResolvedModelCompat: ({ modelId, model }) =>
shouldContributeMistralCompat({ modelId, model }) ? MISTRAL_MODEL_COMPAT_PATCH : undefined,
capabilities: {
transcriptToolCallIdMode: "strict9",
transcriptToolCallIdModelHints: [
"mistral",
"mixtral",
"codestral",
"pixtral",
"devstral",
"ministral",
"mistralai",
],
},
buildReplayPolicy: () => buildMistralReplayPolicy(),
},
register(api) {
api.registerMediaUnderstandingProvider(mistralMediaUnderstandingProvider);

View File

@@ -17,17 +17,18 @@ import {
normalizeProviderId,
type ProviderPlugin,
} from "openclaw/plugin-sdk/provider-model-shared";
import { createOpenAIAttributionHeadersWrapper } from "openclaw/plugin-sdk/provider-stream";
import { fetchCodexUsage } from "openclaw/plugin-sdk/provider-usage";
import { OPENAI_CODEX_DEFAULT_MODEL } from "./default-models.js";
import { resolveCodexAuthIdentity } from "./openai-codex-auth-identity.js";
import { buildOpenAICodexProvider } from "./openai-codex-catalog.js";
import { buildOpenAIReplayPolicy } from "./replay-policy.js";
import {
cloneFirstTemplateModel,
findCatalogTemplate,
isOpenAIApiBaseUrl,
matchesExactOrPrefix,
} from "./shared.js";
import { wrapOpenAICodexProviderStream } from "./stream-hooks.js";
const PROVIDER_ID = "openai-codex";
const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
@@ -270,12 +271,10 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin {
},
resolveDynamicModel: (ctx) => resolveCodexForwardCompatModel(ctx),
buildAuthDoctorHint: (ctx) => buildOpenAICodexAuthDoctorHint(ctx),
capabilities: {
providerFamily: "openai",
},
supportsXHighThinking: ({ modelId }) =>
matchesExactOrPrefix(modelId, OPENAI_CODEX_XHIGH_MODEL_IDS),
isModernModelRef: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_CODEX_MODERN_MODEL_IDS),
buildReplayPolicy: (ctx) => buildOpenAIReplayPolicy(ctx),
prepareExtraParams: (ctx) => {
const transport = ctx.extraParams?.transport;
if (transport === "auto" || transport === "sse" || transport === "websocket") {
@@ -286,7 +285,7 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin {
transport: "auto",
};
},
wrapStreamFn: (ctx) => createOpenAIAttributionHeadersWrapper(ctx.streamFn),
wrapStreamFn: (ctx) => wrapOpenAICodexProviderStream(ctx),
normalizeResolvedModel: (ctx) => {
if (normalizeProviderId(ctx.provider) !== PROVIDER_ID) {
return undefined;

View File

@@ -1,3 +1,5 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import type { Context, Model, SimpleStreamOptions } from "@mariozechner/pi-ai";
import { describe, expect, it, vi } from "vitest";
import { buildOpenAICodexProviderPlugin } from "./openai-codex-provider.js";
import { buildOpenAIProvider } from "./openai-provider.js";
@@ -8,6 +10,44 @@ vi.mock("./openai-codex-provider.runtime.js", () => ({
refreshOpenAICodexToken: refreshOpenAICodexTokenMock,
}));
function runWrappedPayloadCase(params: {
wrap: NonNullable<ReturnType<typeof buildOpenAIProvider>["wrapStreamFn"]>;
provider: string;
modelId: string;
model:
| Model<"openai-responses">
| Model<"openai-codex-responses">
| Model<"azure-openai-responses">;
extraParams?: Record<string, unknown>;
cfg?: Record<string, unknown>;
payload?: Record<string, unknown>;
}) {
const payload = params.payload ?? { store: false };
let capturedOptions: (SimpleStreamOptions & { openaiWsWarmup?: boolean }) | undefined;
const baseStreamFn: StreamFn = (model, _context, options) => {
capturedOptions = options as (SimpleStreamOptions & { openaiWsWarmup?: boolean }) | undefined;
options?.onPayload?.(payload, model);
return {} as ReturnType<StreamFn>;
};
const streamFn = params.wrap({
provider: params.provider,
modelId: params.modelId,
extraParams: params.extraParams,
config: params.cfg as never,
agentDir: "/tmp/openai-provider-test",
streamFn: baseStreamFn,
} as never);
const context: Context = { messages: [] };
void streamFn?.(params.model, context, {});
return {
payload,
options: capturedOptions,
};
}
describe("buildOpenAIProvider", () => {
it("resolves gpt-5.4 mini and nano from GPT-5 small-model templates", () => {
const provider = buildOpenAIProvider();
@@ -172,6 +212,170 @@ describe("buildOpenAIProvider", () => {
).toBe(true);
});
it("owns replay policy for OpenAI and Codex transports", () => {
const provider = buildOpenAIProvider();
const codexProvider = buildOpenAICodexProviderPlugin();
expect(
provider.buildReplayPolicy?.({
provider: "openai",
modelApi: "openai",
modelId: "gpt-5.4",
} as never),
).toEqual({
sanitizeMode: "images-only",
applyAssistantFirstOrderingFix: false,
sanitizeToolCallIds: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
});
expect(
provider.buildReplayPolicy?.({
provider: "openai",
modelApi: "openai-completions",
modelId: "gpt-5.4",
} as never),
).toEqual({
sanitizeMode: "images-only",
applyAssistantFirstOrderingFix: false,
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
validateGeminiTurns: false,
validateAnthropicTurns: false,
});
expect(
codexProvider.buildReplayPolicy?.({
provider: "openai-codex",
modelApi: "openai-codex-responses",
modelId: "gpt-5.4",
} as never),
).toEqual({
sanitizeMode: "images-only",
applyAssistantFirstOrderingFix: false,
sanitizeToolCallIds: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
});
});
it("owns direct OpenAI wrapper composition for responses payloads", () => {
const provider = buildOpenAIProvider();
const extraParams = provider.prepareExtraParams?.({
provider: "openai",
modelId: "gpt-5.4",
extraParams: {
fastMode: true,
serviceTier: "priority",
textVerbosity: "low",
},
} as never);
const result = runWrappedPayloadCase({
wrap: provider.wrapStreamFn as NonNullable<typeof provider.wrapStreamFn>,
provider: "openai",
modelId: "gpt-5.4",
extraParams: extraParams ?? undefined,
model: {
api: "openai-responses",
provider: "openai",
id: "gpt-5.4",
baseUrl: "https://api.openai.com/v1",
} as Model<"openai-responses">,
payload: {
reasoning: { effort: "none" },
},
});
expect(extraParams).toMatchObject({
transport: "auto",
openaiWsWarmup: false,
});
expect(result.payload.service_tier).toBe("priority");
expect(result.payload.text).toEqual({ verbosity: "low" });
expect(result.payload).not.toHaveProperty("reasoning");
});
it("owns Azure OpenAI reasoning compatibility without forcing OpenAI transport defaults", () => {
const provider = buildOpenAIProvider();
const result = runWrappedPayloadCase({
wrap: provider.wrapStreamFn as NonNullable<typeof provider.wrapStreamFn>,
provider: "azure-openai-responses",
modelId: "gpt-5.4",
model: {
api: "azure-openai-responses",
provider: "azure-openai-responses",
id: "gpt-5.4",
baseUrl: "https://example.openai.azure.com/openai/v1",
} as Model<"azure-openai-responses">,
payload: {
reasoning: { effort: "none" },
},
});
expect(result.options?.transport).toBeUndefined();
expect(result.options?.openaiWsWarmup).toBeUndefined();
expect(result.payload).not.toHaveProperty("reasoning");
});
it("owns Codex wrapper composition for responses payloads", () => {
const provider = buildOpenAICodexProviderPlugin();
const result = runWrappedPayloadCase({
wrap: provider.wrapStreamFn as NonNullable<typeof provider.wrapStreamFn>,
provider: "openai-codex",
modelId: "gpt-5.4",
extraParams: {
fastMode: true,
serviceTier: "priority",
text_verbosity: "high",
},
cfg: {
auth: {
profiles: {
"openai-codex:default": {
provider: "openai-codex",
mode: "oauth",
},
},
},
tools: {
web: {
search: {
enabled: true,
openaiCodex: {
enabled: true,
mode: "live",
allowedDomains: ["example.com"],
},
},
},
},
},
model: {
api: "openai-codex-responses",
provider: "openai-codex",
id: "gpt-5.4",
baseUrl: "https://chatgpt.com/backend-api",
} as Model<"openai-codex-responses">,
payload: {
store: false,
text: { verbosity: "medium" },
tools: [{ type: "function", name: "read" }],
},
});
expect(result.payload.store).toBe(false);
expect(result.payload.service_tier).toBe("priority");
expect(result.payload.text).toEqual({ verbosity: "high" });
expect(result.payload.tools).toEqual([
{ type: "function", name: "read" },
{
type: "web_search",
external_web_access: true,
filters: { allowed_domains: ["example.com"] },
},
]);
});
it("falls back to cached codex oauth credentials on accountId extraction failures", async () => {
const provider = buildOpenAICodexProviderPlugin();
const credential = {

View File

@@ -9,17 +9,15 @@ import {
normalizeProviderId,
type ProviderPlugin,
} from "openclaw/plugin-sdk/provider-model-shared";
import {
createOpenAIAttributionHeadersWrapper,
createOpenAIDefaultTransportWrapper,
} from "openclaw/plugin-sdk/provider-stream";
import { applyOpenAIConfig, OPENAI_DEFAULT_MODEL } from "./default-models.js";
import { buildOpenAIReplayPolicy } from "./replay-policy.js";
import {
cloneFirstTemplateModel,
findCatalogTemplate,
isOpenAIApiBaseUrl,
matchesExactOrPrefix,
} from "./shared.js";
import { wrapAzureOpenAIProviderStream, wrapOpenAIProviderStream } from "./stream-hooks.js";
const PROVIDER_ID = "openai";
const OPENAI_GPT_54_MODEL_ID = "gpt-5.4";
@@ -202,6 +200,7 @@ export function buildOpenAIProvider(): ProviderPlugin {
return {
id: PROVIDER_ID,
label: "OpenAI",
hookAliases: ["azure-openai", "azure-openai-responses"],
docsPath: "/providers/models",
envVars: ["OPENAI_API_KEY"],
auth: [
@@ -237,11 +236,25 @@ export function buildOpenAIProvider(): ProviderPlugin {
shouldUseOpenAIResponsesTransport({ provider, api, baseUrl })
? { api: "openai-responses", baseUrl }
: undefined,
capabilities: {
providerFamily: "openai",
buildReplayPolicy: (ctx) => buildOpenAIReplayPolicy(ctx),
prepareExtraParams: (ctx) => {
const transport = ctx.extraParams?.transport;
const hasSupportedTransport =
transport === "auto" || transport === "sse" || transport === "websocket";
const hasExplicitWarmup = typeof ctx.extraParams?.openaiWsWarmup === "boolean";
if (hasSupportedTransport && hasExplicitWarmup) {
return ctx.extraParams;
}
return {
...ctx.extraParams,
...(hasSupportedTransport ? {} : { transport: "auto" }),
...(hasExplicitWarmup ? {} : { openaiWsWarmup: false }),
};
},
wrapStreamFn: (ctx) =>
createOpenAIAttributionHeadersWrapper(createOpenAIDefaultTransportWrapper(ctx.streamFn)),
normalizeProviderId(ctx.provider) === PROVIDER_ID
? wrapOpenAIProviderStream(ctx)
: wrapAzureOpenAIProviderStream(ctx),
supportsXHighThinking: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_XHIGH_MODEL_IDS),
isModernModelRef: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_MODERN_MODEL_IDS),
buildMissingAuthMessage: (ctx) => {

View File

@@ -0,0 +1,24 @@
import type {
ProviderReplayPolicy,
ProviderReplayPolicyContext,
} from "openclaw/plugin-sdk/plugin-entry";
/**
* Returns the provider-owned replay policy for OpenAI-family transports.
*/
export function buildOpenAIReplayPolicy(ctx: ProviderReplayPolicyContext): ProviderReplayPolicy {
return {
sanitizeMode: "images-only",
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(ctx.modelApi === "openai-completions"
? {
sanitizeToolCallIds: true,
toolCallIdMode: "strict" as const,
}
: {
sanitizeToolCallIds: false,
}),
};
}

View File

@@ -0,0 +1,60 @@
import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry";
import {
createCodexNativeWebSearchWrapper,
createOpenAIAttributionHeadersWrapper,
createOpenAIFastModeWrapper,
createOpenAIReasoningCompatibilityWrapper,
createOpenAIResponsesContextManagementWrapper,
createOpenAIServiceTierWrapper,
createOpenAITextVerbosityWrapper,
resolveOpenAIFastMode,
resolveOpenAIServiceTier,
resolveOpenAITextVerbosity,
} from "openclaw/plugin-sdk/provider-stream";
function applySharedOpenAIWrappers(
streamFn: ProviderWrapStreamFnContext["streamFn"],
ctx: ProviderWrapStreamFnContext,
) {
// Transport-default ownership lives in prepareExtraParams. These wrappers stay
// intentionally identical across direct OpenAI, Azure OpenAI, and Codex.
let nextStreamFn = createOpenAIAttributionHeadersWrapper(streamFn);
if (resolveOpenAIFastMode(ctx.extraParams)) {
nextStreamFn = createOpenAIFastModeWrapper(nextStreamFn);
}
const serviceTier = resolveOpenAIServiceTier(ctx.extraParams);
if (serviceTier) {
nextStreamFn = createOpenAIServiceTierWrapper(nextStreamFn, serviceTier);
}
const textVerbosity = resolveOpenAITextVerbosity(ctx.extraParams);
if (textVerbosity) {
nextStreamFn = createOpenAITextVerbosityWrapper(nextStreamFn, textVerbosity);
}
nextStreamFn = createCodexNativeWebSearchWrapper(nextStreamFn, {
config: ctx.config,
agentDir: ctx.agentDir,
});
return createOpenAIResponsesContextManagementWrapper(
createOpenAIReasoningCompatibilityWrapper(nextStreamFn),
ctx.extraParams,
);
}
/** Compose the direct OpenAI wrapper chain inside the owning provider plugin. */
export function wrapOpenAIProviderStream(ctx: ProviderWrapStreamFnContext) {
return applySharedOpenAIWrappers(ctx.streamFn, ctx);
}
/** Compose the Azure OpenAI wrapper chain without direct OpenAI transport defaults. */
export function wrapAzureOpenAIProviderStream(ctx: ProviderWrapStreamFnContext) {
return applySharedOpenAIWrappers(ctx.streamFn, ctx);
}
/** Compose the Codex-specific wrapper chain inside the owning provider plugin. */
export function wrapOpenAICodexProviderStream(ctx: ProviderWrapStreamFnContext) {
return applySharedOpenAIWrappers(ctx.streamFn, ctx);
}

View File

@@ -4,6 +4,23 @@ import { applyOpencodeGoConfig, OPENCODE_GO_DEFAULT_MODEL_REF } from "./api.js";
const PROVIDER_ID = "opencode-go";
function buildOpencodeGoReplayPolicy(modelId?: string) {
const normalizedModelId = modelId?.toLowerCase() ?? "";
return {
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(normalizedModelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
};
}
export default definePluginEntry({
id: PROVIDER_ID,
name: "OpenCode Go Provider",
@@ -43,11 +60,7 @@ export default definePluginEntry({
},
}),
],
capabilities: {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
buildReplayPolicy: ({ modelId }) => buildOpencodeGoReplayPolicy(modelId),
isModernModelRef: () => true,
});
},

View File

@@ -14,6 +14,23 @@ function isModernOpencodeModel(modelId: string): boolean {
return !matchesExactOrPrefix(lower, MINIMAX_MODERN_MODEL_MATCHERS);
}
function buildOpencodeReplayPolicy(modelId?: string) {
const normalizedModelId = modelId?.toLowerCase() ?? "";
return {
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(normalizedModelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
};
}
export default definePluginEntry({
id: PROVIDER_ID,
name: "OpenCode Zen Provider",
@@ -54,11 +71,7 @@ export default definePluginEntry({
},
}),
],
capabilities: {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
buildReplayPolicy: ({ modelId }) => buildOpencodeReplayPolicy(modelId),
isModernModelRef: ({ modelId }) => isModernOpencodeModel(modelId),
});
},

View File

@@ -74,6 +74,23 @@ function isOpenRouterCacheTtlModel(modelId: string): boolean {
return OPENROUTER_CACHE_TTL_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix));
}
function buildOpenRouterReplayPolicy(modelId?: string) {
const normalizedModelId = modelId?.toLowerCase() ?? "";
return {
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(normalizedModelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
};
}
export default definePluginEntry({
id: "openrouter",
name: "OpenRouter Provider",
@@ -125,11 +142,7 @@ export default definePluginEntry({
prepareDynamicModel: async (ctx) => {
await loadOpenRouterModelCapabilities(ctx.modelId);
},
capabilities: {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
buildReplayPolicy: ({ modelId }) => buildOpenRouterReplayPolicy(modelId),
isModernModelRef: () => true,
wrapStreamFn: (ctx) => {
let streamFn = ctx.streamFn;

View File

@@ -312,6 +312,30 @@ describe("sanitizeSessionMessagesImages", () => {
expect((content?.[1] as { thought_signature?: unknown })?.thought_signature).toBe("AQID");
});
it("still strips signatures in images-only mode when replay policy requests it", async () => {
const input = castAgentMessages([
{
role: "assistant",
content: [
{ type: "thinking", thinking: "internal", thought_signature: "msg_abc123" },
{ type: "text", text: "visible" },
],
},
]);
const out = await sanitizeSessionMessagesImages(input, "test", {
sanitizeMode: "images-only",
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
});
const content = (out[0] as { content?: Array<{ thought_signature?: unknown }> }).content;
expect(content).toHaveLength(2);
expect(content?.[0]?.thought_signature).toBeUndefined();
});
it("preserves interleaved thinking block order when signatures are preserved", async () => {
const input = castAgentMessages([
{

View File

@@ -120,18 +120,18 @@ export async function sanitizeSessionMessagesImages(
}
const content = assistantMsg.content;
if (Array.isArray(content)) {
const strippedContent = options?.preserveSignatures
? content // Keep signatures for Antigravity Claude
: stripThoughtSignatures(content, options?.sanitizeThoughtSignatures); // Strip for Gemini
if (!allowNonImageSanitization) {
const nextContent = (await sanitizeContentBlocksImages(
content as unknown as ContentBlock[],
strippedContent as unknown as ContentBlock[],
label,
imageSanitization,
)) as unknown as typeof assistantMsg.content;
out.push({ ...assistantMsg, content: nextContent });
continue;
}
const strippedContent = options?.preserveSignatures
? content // Keep signatures for Antigravity Claude
: stripThoughtSignatures(content, options?.sanitizeThoughtSignatures); // Strip for Gemini
const filteredContent =
options?.preserveSignatures &&

View File

@@ -16,28 +16,6 @@ import {
createOpenRouterWrapper,
isProxyReasoningUnsupported,
} from "./pi-embedded-runner/proxy-stream-wrappers.js";
import type { ProviderCapabilities } from "./provider-capabilities.js";
import { __testing as providerCapabilitiesTesting } from "./provider-capabilities.js";
const resolveProviderCapabilitiesWithPluginMock = vi.fn(
(params: {
provider: string;
config?: import("../config/config.js").OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): Partial<ProviderCapabilities> | undefined => {
if (
params.provider === "workspace-anthropic-proxy" &&
params.workspaceDir === "/tmp/workspace-capabilities"
) {
return {
anthropicToolSchemaMode: "openai-functions",
anthropicToolChoiceMode: "openai-string-modes",
};
}
return undefined;
},
);
const XAI_FAST_MODEL_IDS = new Map<string, string>([
["grok-3", "grok-3-fast"],
@@ -76,7 +54,69 @@ import {
resolveExtraParams,
resolvePreparedExtraParams,
} from "./pi-embedded-runner.js";
import { createAnthropicToolPayloadCompatibilityWrapper } from "./pi-embedded-runner/anthropic-family-tool-payload-compat.js";
import {
createBedrockNoCacheWrapper,
isAnthropicBedrockModel,
} from "./pi-embedded-runner/bedrock-stream-wrappers.js";
import { createGoogleThinkingPayloadWrapper } from "./pi-embedded-runner/google-stream-wrappers.js";
import { log } from "./pi-embedded-runner/logger.js";
import { createMinimaxFastModeWrapper } from "./pi-embedded-runner/minimax-stream-wrappers.js";
import {
createMoonshotThinkingWrapper,
resolveMoonshotThinkingType,
} from "./pi-embedded-runner/moonshot-stream-wrappers.js";
import {
createCodexNativeWebSearchWrapper,
createOpenAIAttributionHeadersWrapper,
createOpenAIDefaultTransportWrapper,
createOpenAIFastModeWrapper,
createOpenAIReasoningCompatibilityWrapper,
createOpenAIResponsesContextManagementWrapper,
createOpenAIServiceTierWrapper,
createOpenAITextVerbosityWrapper,
resolveOpenAIFastMode,
resolveOpenAIServiceTier,
resolveOpenAITextVerbosity,
} from "./pi-embedded-runner/openai-stream-wrappers.js";
type WrapProviderStreamFnParams = Parameters<
typeof import("../plugins/provider-runtime.js").wrapProviderStreamFn
>[0];
function createTestOpenAIProviderWrapper(
params: WrapProviderStreamFnParams,
withDefaultTransport: boolean,
): StreamFn {
let streamFn = params.context.streamFn;
if (withDefaultTransport) {
streamFn = createOpenAIDefaultTransportWrapper(streamFn);
}
streamFn = createOpenAIAttributionHeadersWrapper(streamFn);
if (resolveOpenAIFastMode(params.context.extraParams)) {
streamFn = createOpenAIFastModeWrapper(streamFn);
}
const serviceTier = resolveOpenAIServiceTier(params.context.extraParams);
if (serviceTier) {
streamFn = createOpenAIServiceTierWrapper(streamFn, serviceTier);
}
const textVerbosity = resolveOpenAITextVerbosity(params.context.extraParams);
if (textVerbosity) {
streamFn = createOpenAITextVerbosityWrapper(streamFn, textVerbosity);
}
streamFn = createCodexNativeWebSearchWrapper(streamFn, {
config: params.context.config,
agentDir: params.context.agentDir,
});
return createOpenAIResponsesContextManagementWrapper(
createOpenAIReasoningCompatibilityWrapper(streamFn),
params.context.extraParams,
);
}
beforeEach(() => {
extraParamsTesting.setProviderRuntimeDepsForTest({
@@ -94,6 +134,45 @@ beforeEach(() => {
};
},
wrapProviderStreamFn: (params) => {
if (params.provider === "openai") {
return createTestOpenAIProviderWrapper(params, true);
}
if (params.provider === "openai-codex") {
return createTestOpenAIProviderWrapper(params, false);
}
if (params.provider === "azure-openai" || params.provider === "azure-openai-responses") {
return createTestOpenAIProviderWrapper(params, false);
}
if (params.provider === "amazon-bedrock") {
return isAnthropicBedrockModel(params.context.modelId)
? params.context.streamFn
: createBedrockNoCacheWrapper(params.context.streamFn);
}
if (params.provider === "moonshot") {
const thinkingType = resolveMoonshotThinkingType({
configuredThinking: params.context.extraParams?.thinking,
thinkingLevel: params.context.thinkingLevel,
});
return createMoonshotThinkingWrapper(params.context.streamFn, thinkingType);
}
if (params.provider === "google") {
return createGoogleThinkingPayloadWrapper(
params.context.streamFn,
params.context.thinkingLevel,
);
}
if (params.provider === "kimi") {
return createAnthropicToolPayloadCompatibilityWrapper(params.context.streamFn, {
toolSchemaMode: "openai-functions",
toolChoiceMode: "openai-string-modes",
});
}
if (params.provider === "minimax" || params.provider === "minimax-portal") {
return createMinimaxFastModeWrapper(
params.context.streamFn,
params.context.extraParams?.fastMode === true,
);
}
if (params.provider === "ollama") {
return createConfiguredOllamaCompatNumCtxWrapper(params.context);
}
@@ -151,15 +230,10 @@ beforeEach(() => {
return createOpenRouterSystemCacheWrapper(createOpenRouterWrapper(streamFn, thinkingLevel));
},
});
providerCapabilitiesTesting.setResolveProviderCapabilitiesWithPluginForTest(
resolveProviderCapabilitiesWithPluginMock,
);
resolveProviderCapabilitiesWithPluginMock.mockClear();
});
afterEach(() => {
extraParamsTesting.resetProviderRuntimeDepsForTest();
providerCapabilitiesTesting.resetDepsForTests();
});
describe("resolveExtraParams", () => {
@@ -1090,7 +1164,7 @@ describe("applyExtraParamsToAgent", () => {
});
});
it("rewrites tool schema for Kimi to the OpenAI function shape", () => {
it("rewrites anthropic tool payloads for Kimi", () => {
const payloads: Record<string, unknown>[] = [];
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {
@@ -1200,16 +1274,7 @@ describe("applyExtraParamsToAgent", () => {
payloads.push(payload);
return {} as ReturnType<StreamFn>;
};
const agent = { streamFn: baseStreamFn };
applyExtraParamsToAgent(
agent,
undefined,
"custom-anthropic-proxy",
"proxy-model",
undefined,
"low",
);
const streamFn = createAnthropicToolPayloadCompatibilityWrapper(baseStreamFn);
const model = {
api: "anthropic-messages",
@@ -1220,7 +1285,7 @@ describe("applyExtraParamsToAgent", () => {
},
} as unknown as Model<"anthropic-messages">;
const context: Context = { messages: [] };
void agent.streamFn?.(model, context, {});
void streamFn(model, context, {});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.tools).toEqual([
@@ -1235,7 +1300,7 @@ describe("applyExtraParamsToAgent", () => {
]);
});
it("uses workspace plugin capability metadata for anthropic tool payload normalization", () => {
it("lets provider-owned wrappers normalize anthropic tool payloads", () => {
const payloads: Record<string, unknown>[] = [];
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {
@@ -1254,20 +1319,11 @@ describe("applyExtraParamsToAgent", () => {
};
const agent = { streamFn: baseStreamFn };
applyExtraParamsToAgent(
agent,
{ plugins: { enabled: true } },
"workspace-anthropic-proxy",
"proxy-model",
undefined,
"low",
undefined,
"/tmp/workspace-capabilities",
);
applyExtraParamsToAgent(agent, undefined, "kimi", "proxy-model", undefined, "low");
const model = {
api: "anthropic-messages",
provider: "workspace-anthropic-proxy",
provider: "kimi",
id: "proxy-model",
} as Model<"anthropic-messages">;
const context: Context = { messages: [] };
@@ -1285,69 +1341,6 @@ describe("applyExtraParamsToAgent", () => {
},
]);
expect(payloads[0]?.tool_choice).toBe("required");
expect(resolveProviderCapabilitiesWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "workspace-anthropic-proxy",
workspaceDir: "/tmp/workspace-capabilities",
}),
);
});
it("normalizes kimi-coding anthropic tool payloads to OpenAI function shape", () => {
const payloads: Record<string, unknown>[] = [];
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {
tools: [
{
name: "exec",
description: "Execute a shell command",
input_schema: {
type: "object",
properties: {
command: { type: "string" },
},
required: ["command"],
},
},
],
tool_choice: { type: "any" },
};
options?.onPayload?.(payload, _model);
payloads.push(payload);
return {} as ReturnType<StreamFn>;
};
const agent = { streamFn: baseStreamFn };
applyExtraParamsToAgent(agent, undefined, "kimi-coding", "k2p5", undefined, "low");
const model = {
api: "anthropic-messages",
provider: "kimi-coding",
id: "k2p5",
} as Model<"anthropic-messages">;
const context: Context = { messages: [] };
void agent.streamFn?.(model, context, {});
expect(payloads).toHaveLength(1);
expect(payloads[0]).toMatchObject({
tools: [
{
type: "function",
function: {
name: "exec",
description: "Execute a shell command",
parameters: {
type: "object",
properties: {
command: { type: "string" },
},
required: ["command"],
},
},
},
],
tool_choice: "auto",
});
});
it("sanitizes invalid Atproxy Gemini negative thinking budgets", () => {
@@ -2844,24 +2837,6 @@ describe("applyExtraParamsToAgent", () => {
expect(payload).not.toHaveProperty("service_tier");
});
it("does not inject Anthropic service_tier for spoofed Anthropic-looking hosts", () => {
const payload = runResponsesPayloadMutationCase({
applyProvider: "anthropic",
applyModelId: "claude-sonnet-4-5",
extraParamsOverride: {
serviceTier: "standard_only",
},
model: {
api: "anthropic-messages",
provider: "anthropic",
id: "claude-sonnet-4-5",
baseUrl: "https://api.anthropic.com.attacker.example/v1",
} as unknown as Model<"anthropic-messages">,
payload: {},
});
expect(payload).not.toHaveProperty("service_tier");
});
it("maps fast mode to priority service_tier for openai-codex responses", () => {
const payload = runResponsesPayloadMutationCase({
applyProvider: "openai-codex",

View File

@@ -1,62 +0,0 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import { SessionManager } from "@mariozechner/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import { applyGoogleTurnOrderingFix } from "./pi-embedded-runner.js";
import { castAgentMessage } from "./test-helpers/agent-message-fixtures.js";
describe("applyGoogleTurnOrderingFix", () => {
const makeAssistantFirst = (): AgentMessage[] => [
castAgentMessage({
role: "assistant",
content: [{ type: "toolCall", id: "call_1", name: "exec", arguments: {} }],
}),
];
it("prepends a bootstrap once and records a marker for Google models", () => {
const sessionManager = SessionManager.inMemory();
const warn = vi.fn();
const input = makeAssistantFirst();
const first = applyGoogleTurnOrderingFix({
messages: input,
modelApi: "google-generative-ai",
sessionManager,
sessionId: "session:1",
warn,
});
expect(first.messages[0]?.role).toBe("user");
expect(first.messages[1]?.role).toBe("assistant");
expect(warn).toHaveBeenCalledTimes(1);
expect(
sessionManager
.getEntries()
.some(
(entry) =>
entry.type === "custom" && entry.customType === "google-turn-ordering-bootstrap",
),
).toBe(true);
applyGoogleTurnOrderingFix({
messages: input,
modelApi: "google-generative-ai",
sessionManager,
sessionId: "session:1",
warn,
});
expect(warn).toHaveBeenCalledTimes(1);
});
it("skips non-Google models", () => {
const sessionManager = SessionManager.inMemory();
const warn = vi.fn();
const input = makeAssistantFirst();
const result = applyGoogleTurnOrderingFix({
messages: input,
modelApi: "openai",
sessionManager,
sessionId: "session:2",
warn,
});
expect(result.messages).toBe(input);
expect(warn).not.toHaveBeenCalled();
});
});

View File

@@ -4,7 +4,7 @@ import {
makeInMemorySessionManager,
makeModelSnapshotEntry,
} from "./pi-embedded-runner.sanitize-session-history.test-harness.js";
import { sanitizeSessionHistory } from "./pi-embedded-runner/google.js";
import { sanitizeSessionHistory } from "./pi-embedded-runner/replay-history.js";
import { castAgentMessage } from "./test-helpers/agent-message-fixtures.js";
describe("sanitizeSessionHistory openai tool id preservation", () => {

View File

@@ -63,7 +63,7 @@ export async function loadSanitizeSessionHistoryWithCleanMocks(): Promise<Saniti
vi.resetAllMocks();
const mockedHelpers = await import("./pi-embedded-helpers.js");
vi.mocked(mockedHelpers.sanitizeSessionMessagesImages).mockImplementation(async (msgs) => msgs);
const mod = await import("./pi-embedded-runner/google.js");
const mod = await import("./pi-embedded-runner/replay-history.js");
return {
sanitizeSessionHistory: mod.sanitizeSessionHistory,
mockedHelpers,

View File

@@ -25,20 +25,65 @@ vi.mock("./pi-embedded-helpers.js", async () => ({
}));
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderCapabilitiesWithPlugin: ({ provider }: { provider?: string }) =>
provider === "openrouter"
resolveProviderRuntimePlugin: ({ provider }: { provider?: string }) =>
provider === "openrouter" || provider === "github-copilot"
? {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
buildReplayPolicy: (context?: { modelId?: string | null }) => {
const modelId = String(context?.modelId ?? "").toLowerCase();
if (provider === "openrouter") {
return {
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(modelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
};
}
if (provider === "github-copilot" && modelId.includes("claude")) {
return {
dropThinkingBlocks: true,
};
}
return undefined;
},
}
: provider === "github-copilot"
? {
dropThinkingBlockModelHints: ["claude"],
}
: undefined,
sanitizeProviderReplayHistoryWithPlugin: vi.fn(async ({ messages }) => messages),
resolveProviderReplayPolicyWithPlugin: vi.fn(() => undefined),
: undefined,
sanitizeProviderReplayHistoryWithPlugin: vi.fn(
async ({
provider,
context,
}: {
provider?: string;
context: {
messages: AgentMessage[];
sessionState?: {
appendCustomEntry(customType: string, data: unknown): void;
};
};
}) => {
if (
provider &&
provider.startsWith("google") &&
context.messages[0]?.role === "assistant" &&
context.sessionState
) {
context.sessionState.appendCustomEntry("google-turn-ordering-bootstrap", {
timestamp: Date.now(),
});
return [
{ role: "user", content: "(session bootstrap)" } as AgentMessage,
...context.messages,
];
}
return context.messages;
},
),
validateProviderReplayTurnsWithPlugin: vi.fn(() => undefined),
}));
@@ -226,6 +271,33 @@ describe("sanitizeSessionHistory", () => {
expect(result).toEqual(mockMessages);
});
it("lets Google provider hooks prepend a bootstrap turn and persist a marker", async () => {
vi.mocked(mockedHelpers.isGoogleModelApi).mockReturnValue(true);
const sessionEntries: Array<{ type: string; customType: string; data: unknown }> = [];
const sessionManager = makeInMemorySessionManager(sessionEntries);
const result = await sanitizeSessionHistory({
messages: castAgentMessages([
{
role: "assistant",
content: [{ type: "text", text: "hello from previous turn" }],
},
]),
modelApi: "google-generative-ai",
provider: "google-vertex",
sessionManager,
sessionId: TEST_SESSION_ID,
});
expect(result[0]).toMatchObject({
role: "user",
content: "(session bootstrap)",
});
expect(
sessionEntries.some((entry) => entry.customType === "google-turn-ordering-bootstrap"),
).toBe(true);
});
it("passes simple user-only history through for Mistral models", async () => {
setNonGoogleModelApi();

View File

@@ -7,7 +7,6 @@ export {
resolvePreparedExtraParams,
} from "./pi-embedded-runner/extra-params.js";
export { applyGoogleTurnOrderingFix } from "./pi-embedded-runner/google.js";
export {
getDmHistoryLimitFromSessionKey,
getHistoryLimitFromSessionKey,

View File

@@ -1,14 +1,12 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import { streamSimple } from "@mariozechner/pi-ai";
import type { OpenClawConfig } from "../../config/config.js";
import {
type ProviderCapabilityLookupOptions,
requiresOpenAiCompatibleAnthropicToolPayload,
usesOpenAiFunctionAnthropicToolSchema,
usesOpenAiStringModeAnthropicToolChoice,
} from "../provider-capabilities.js";
type AnthropicToolSchemaMode = "openai-functions";
type AnthropicToolChoiceMode = "openai-string-modes";
type AnthropicToolPayloadResolverOptions = ProviderCapabilityLookupOptions;
type AnthropicToolPayloadCompatibilityOptions = {
toolSchemaMode?: AnthropicToolSchemaMode;
toolChoiceMode?: AnthropicToolChoiceMode;
};
function hasOpenAiAnthropicToolPayloadCompatFlag(model: { compat?: unknown }): boolean {
if (!model.compat || typeof model.compat !== "object" || Array.isArray(model.compat)) {
@@ -24,54 +22,40 @@ function hasOpenAiAnthropicToolPayloadCompatFlag(model: { compat?: unknown }): b
function requiresAnthropicToolPayloadCompatibilityForModel(
model: {
api?: unknown;
provider?: unknown;
compat?: unknown;
},
options?: AnthropicToolPayloadResolverOptions,
options?: AnthropicToolPayloadCompatibilityOptions,
): boolean {
if (model.api !== "anthropic-messages") {
return false;
}
if (
typeof model.provider === "string" &&
requiresOpenAiCompatibleAnthropicToolPayload(model.provider, options)
) {
return true;
}
return hasOpenAiAnthropicToolPayloadCompatFlag(model);
return (
Boolean(options?.toolSchemaMode || options?.toolChoiceMode) ||
hasOpenAiAnthropicToolPayloadCompatFlag(model)
);
}
function usesOpenAiFunctionAnthropicToolSchemaForModel(
model: {
provider?: unknown;
compat?: unknown;
},
options?: AnthropicToolPayloadResolverOptions,
options?: AnthropicToolPayloadCompatibilityOptions,
): boolean {
if (
typeof model.provider === "string" &&
usesOpenAiFunctionAnthropicToolSchema(model.provider, options)
) {
return true;
}
return hasOpenAiAnthropicToolPayloadCompatFlag(model);
return (
options?.toolSchemaMode === "openai-functions" || hasOpenAiAnthropicToolPayloadCompatFlag(model)
);
}
function usesOpenAiStringModeAnthropicToolChoiceForModel(
model: {
provider?: unknown;
compat?: unknown;
},
options?: AnthropicToolPayloadResolverOptions,
options?: AnthropicToolPayloadCompatibilityOptions,
): boolean {
if (
typeof model.provider === "string" &&
usesOpenAiStringModeAnthropicToolChoice(model.provider, options)
) {
return true;
}
return hasOpenAiAnthropicToolPayloadCompatFlag(model);
return (
options?.toolChoiceMode === "openai-string-modes" ||
hasOpenAiAnthropicToolPayloadCompatFlag(model)
);
}
function normalizeOpenAiFunctionAnthropicToolDefinition(
@@ -141,11 +125,7 @@ function normalizeOpenAiStringModeAnthropicToolChoice(toolChoice: unknown): unkn
export function createAnthropicToolPayloadCompatibilityWrapper(
baseStreamFn: StreamFn | undefined,
resolverOptions?: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
},
options?: AnthropicToolPayloadCompatibilityOptions,
): StreamFn {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, streamOptions) => {
@@ -156,32 +136,18 @@ export function createAnthropicToolPayloadCompatibilityWrapper(
if (
payload &&
typeof payload === "object" &&
requiresAnthropicToolPayloadCompatibilityForModel(model, {
config: resolverOptions?.config,
workspaceDir: resolverOptions?.workspaceDir,
env: resolverOptions?.env,
})
requiresAnthropicToolPayloadCompatibilityForModel(model, options)
) {
const payloadObj = payload as Record<string, unknown>;
if (
Array.isArray(payloadObj.tools) &&
usesOpenAiFunctionAnthropicToolSchemaForModel(model, {
config: resolverOptions?.config,
workspaceDir: resolverOptions?.workspaceDir,
env: resolverOptions?.env,
})
usesOpenAiFunctionAnthropicToolSchemaForModel(model, options)
) {
payloadObj.tools = payloadObj.tools
.map((tool) => normalizeOpenAiFunctionAnthropicToolDefinition(tool))
.filter((tool): tool is Record<string, unknown> => !!tool);
}
if (
usesOpenAiStringModeAnthropicToolChoiceForModel(model, {
config: resolverOptions?.config,
workspaceDir: resolverOptions?.workspaceDir,
env: resolverOptions?.env,
})
) {
if (usesOpenAiStringModeAnthropicToolChoiceForModel(model, options)) {
payloadObj.tool_choice = normalizeOpenAiStringModeAnthropicToolChoice(
payloadObj.tool_choice,
);

View File

@@ -330,10 +330,14 @@ export async function loadCompactHooksHarness(): Promise<{
createOpenClawCodingTools: createOpenClawCodingToolsMock,
}));
vi.doMock("./google.js", () => ({
logToolSchemasForGoogle: vi.fn(),
vi.doMock("./replay-history.js", () => ({
sanitizeSessionHistory: sanitizeSessionHistoryMock,
sanitizeToolsForGoogle: vi.fn(({ tools }: { tools: unknown[] }) => tools),
validateReplayTurns: vi.fn(async ({ messages }: { messages: unknown[] }) => messages),
}));
vi.doMock("./tool-schema-runtime.js", () => ({
logProviderToolSchemaDiagnostics: vi.fn(),
normalizeProviderToolSchemas: vi.fn(({ tools }: { tools: unknown[] }) => tools),
}));
vi.doMock("./tool-split.js", () => ({

View File

@@ -61,10 +61,6 @@ import {
} from "../pi-hooks/compaction-safeguard-runtime.js";
import { createPreparedEmbeddedPiSettingsManager } from "../pi-project-settings.js";
import { createOpenClawCodingTools } from "../pi-tools.js";
import {
resolveProviderRequestConfig,
sanitizeRuntimeProviderRequestOverrides,
} from "../provider-request-config.js";
import { registerProviderStreamForModel } from "../provider-stream.js";
import { ensureRuntimePluginsLoaded } from "../runtime-plugins.js";
import { resolveSandboxContext } from "../sandbox.js";
@@ -102,17 +98,12 @@ import {
} from "./compaction-safety-timeout.js";
import { runContextEngineMaintenance } from "./context-engine-maintenance.js";
import { buildEmbeddedExtensionFactories } from "./extensions.js";
import {
logToolSchemasForGoogle,
sanitizeSessionHistory,
sanitizeToolsForGoogle,
validateReplayTurns,
} from "./google.js";
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js";
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
import { log } from "./logger.js";
import { buildEmbeddedMessageActionDiscoveryInput } from "./message-action-discovery-input.js";
import { buildModelAliasLines, resolveModelAsync } from "./model.js";
import { sanitizeSessionHistory, validateReplayTurns } from "./replay-history.js";
import { buildEmbeddedSandboxInfo } from "./sandbox-info.js";
import { prewarmSessionFile, trackSessionManagerAccess } from "./session-manager-cache.js";
import { truncateSessionAfterCompaction } from "./session-truncation.js";
@@ -123,6 +114,10 @@ import {
createSystemPromptOverride,
} from "./system-prompt.js";
import { collectAllowedToolNames } from "./tool-name-allowlist.js";
import {
logProviderToolSchemaDiagnostics,
normalizeProviderToolSchemas,
} from "./tool-schema-runtime.js";
import { splitSdkTools } from "./tool-split.js";
import type { EmbeddedPiCompactResult } from "./types.js";
import { describeUnknownError, mapThinkingLevel } from "./utils.js";
@@ -364,24 +359,8 @@ export async function compactEmbeddedPiSessionDirect(
profileId: apiKeyInfo.profileId,
},
});
if (preparedAuth?.baseUrl || preparedAuth?.request) {
const runtimeRequestConfig = resolveProviderRequestConfig({
provider: runtimeModel.provider,
api: runtimeModel.api,
baseUrl: preparedAuth?.baseUrl ?? runtimeModel.baseUrl,
providerHeaders:
runtimeModel.headers && typeof runtimeModel.headers === "object"
? runtimeModel.headers
: undefined,
request: sanitizeRuntimeProviderRequestOverrides(preparedAuth?.request),
capability: "llm",
transport: "stream",
});
runtimeModel = {
...runtimeModel,
...(preparedAuth?.baseUrl ? { baseUrl: preparedAuth.baseUrl } : {}),
...(runtimeRequestConfig.headers ? { headers: runtimeRequestConfig.headers } : {}),
};
if (preparedAuth?.baseUrl) {
runtimeModel = { ...runtimeModel, baseUrl: preparedAuth.baseUrl };
}
const runtimeApiKey = preparedAuth?.apiKey ?? apiKeyInfo.apiKey;
hasRuntimeAuthExchange = Boolean(preparedAuth?.apiKey);
@@ -502,7 +481,7 @@ export async function compactEmbeddedPiSessionDirect(
modelAuthMode: resolveModelAuthMode(model.provider, params.config),
});
const toolsEnabled = supportsModelTools(runtimeModel);
const tools = sanitizeToolsForGoogle({
const tools = normalizeProviderToolSchemas({
tools: toolsEnabled ? toolsRaw : [],
provider,
config: params.config,
@@ -535,7 +514,7 @@ export async function compactEmbeddedPiSessionDirect(
...(bundleLspRuntime?.tools ?? []),
];
const allowedToolNames = collectAllowedToolNames({ tools: effectiveTools });
logToolSchemasForGoogle({
logProviderToolSchemaDiagnostics({
tools: effectiveTools,
provider,
config: params.config,

View File

@@ -10,31 +10,13 @@ import {
} from "../../plugins/provider-runtime.js";
import type { ProviderRuntimeModel } from "../../plugins/types.js";
import { resolveCacheRetention } from "./anthropic-cache-retention.js";
import { createAnthropicToolPayloadCompatibilityWrapper } from "./anthropic-family-tool-payload-compat.js";
import { createBedrockNoCacheWrapper, isAnthropicBedrockModel } from "./bedrock-stream-wrappers.js";
import { createGoogleThinkingPayloadWrapper } from "./google-stream-wrappers.js";
import { log } from "./logger.js";
import { createMinimaxFastModeWrapper } from "./minimax-stream-wrappers.js";
import {
createMoonshotThinkingWrapper,
resolveMoonshotThinkingType,
createSiliconFlowThinkingWrapper,
shouldApplyMoonshotPayloadCompat,
shouldApplySiliconFlowThinkingOffCompat,
} from "./moonshot-stream-wrappers.js";
import {
createOpenAIAttributionHeadersWrapper,
createCodexNativeWebSearchWrapper,
createOpenAIDefaultTransportWrapper,
createOpenAIFastModeWrapper,
createOpenAIReasoningCompatibilityWrapper,
createOpenAIResponsesContextManagementWrapper,
createOpenAIServiceTierWrapper,
createOpenAITextVerbosityWrapper,
resolveOpenAIFastMode,
resolveOpenAIServiceTier,
resolveOpenAITextVerbosity,
} from "./openai-stream-wrappers.js";
import { createOpenAIResponsesContextManagementWrapper } from "./openai-stream-wrappers.js";
import { streamWithPayloadPatch } from "./stream-payload-utils.js";
const defaultProviderRuntimeDeps = {
@@ -198,7 +180,6 @@ function createStreamFnWithExtraParams(
baseStreamFn: StreamFn | undefined,
extraParams: Record<string, unknown> | undefined,
provider: string,
modelApi?: string,
): StreamFn | undefined {
if (!extraParams || Object.keys(extraParams).length === 0) {
return undefined;
@@ -224,7 +205,7 @@ function createStreamFnWithExtraParams(
if (typeof extraParams.openaiWsWarmup === "boolean") {
streamParams.openaiWsWarmup = extraParams.openaiWsWarmup;
}
const cacheRetention = resolveCacheRetention(extraParams, provider, modelApi);
const cacheRetention = resolveCacheRetention(extraParams, provider);
if (cacheRetention) {
streamParams.cacheRetention = cacheRetention;
}
@@ -305,19 +286,10 @@ type ApplyExtraParamsContext = {
};
function applyPrePluginStreamWrappers(ctx: ApplyExtraParamsContext): void {
if (ctx.provider === "openai" || ctx.provider === "openai-codex") {
if (ctx.provider === "openai") {
// Default OpenAI Responses to WebSocket-first with transparent SSE fallback.
ctx.agent.streamFn = createOpenAIDefaultTransportWrapper(ctx.agent.streamFn);
}
ctx.agent.streamFn = createOpenAIAttributionHeadersWrapper(ctx.agent.streamFn);
}
const wrappedStreamFn = createStreamFnWithExtraParams(
ctx.agent.streamFn,
ctx.effectiveExtraParams,
ctx.provider,
ctx.model?.api,
);
if (wrappedStreamFn) {
@@ -337,111 +309,25 @@ function applyPrePluginStreamWrappers(ctx: ApplyExtraParamsContext): void {
);
ctx.agent.streamFn = createSiliconFlowThinkingWrapper(ctx.agent.streamFn);
}
ctx.agent.streamFn = createAnthropicToolPayloadCompatibilityWrapper(ctx.agent.streamFn, {
config: ctx.cfg,
workspaceDir: ctx.workspaceDir,
});
}
function applyPostPluginStreamWrappers(
ctx: ApplyExtraParamsContext & { providerWrapperHandled: boolean },
): void {
if (
!ctx.providerWrapperHandled &&
shouldApplyMoonshotPayloadCompat({ provider: ctx.provider, modelId: ctx.modelId })
) {
// Preserve the legacy Moonshot compatibility path when no plugin wrapper
// actually handled the stream function. This mainly covers tests and
// disabled plugins for the native Moonshot provider.
const thinkingType = resolveMoonshotThinkingType({
configuredThinking: ctx.effectiveExtraParams?.thinking,
thinkingLevel: ctx.thinkingLevel,
});
ctx.agent.streamFn = createMoonshotThinkingWrapper(ctx.agent.streamFn, thinkingType);
}
if (!ctx.providerWrapperHandled) {
// Guard Google-family payloads against invalid negative thinking budgets
// emitted by upstream model-ID heuristics for Gemini 3.1 variants.
ctx.agent.streamFn = createGoogleThinkingPayloadWrapper(ctx.agent.streamFn, ctx.thinkingLevel);
if (ctx.provider === "amazon-bedrock" && !isAnthropicBedrockModel(ctx.modelId)) {
log.debug(
`disabling prompt caching for non-Anthropic Bedrock model ${ctx.provider}/${ctx.modelId}`,
);
ctx.agent.streamFn = createBedrockNoCacheWrapper(ctx.agent.streamFn);
}
// Guard Google payloads against invalid negative thinking budgets emitted by
// upstream model-ID heuristics for Gemini 3.1 variants.
ctx.agent.streamFn = createGoogleThinkingPayloadWrapper(ctx.agent.streamFn, ctx.thinkingLevel);
if (typeof ctx.effectiveExtraParams?.fastMode === "boolean") {
log.debug(
`applying MiniMax fast mode=${ctx.effectiveExtraParams.fastMode} for ${ctx.provider}/${ctx.modelId}`,
);
ctx.agent.streamFn = createMinimaxFastModeWrapper(
// Work around upstream pi-ai hardcoding `store: false` for Responses API.
// Force `store=true` for direct OpenAI Responses models and auto-enable
// server-side compaction for compatible Responses payloads.
ctx.agent.streamFn = createOpenAIResponsesContextManagementWrapper(
ctx.agent.streamFn,
ctx.effectiveExtraParams.fastMode,
ctx.effectiveExtraParams,
);
}
const openAIFastMode = resolveOpenAIFastMode(ctx.effectiveExtraParams);
if (openAIFastMode) {
log.debug(`applying OpenAI fast mode for ${ctx.provider}/${ctx.modelId}`);
ctx.agent.streamFn = createOpenAIFastModeWrapper(ctx.agent.streamFn);
}
if (ctx.provider === "openai" || ctx.provider === "openai-codex") {
const openAIServiceTier = resolveOpenAIServiceTier(ctx.effectiveExtraParams);
if (openAIServiceTier) {
log.debug(
`applying OpenAI service_tier=${openAIServiceTier} for ${ctx.provider}/${ctx.modelId}`,
);
ctx.agent.streamFn = createOpenAIServiceTierWrapper(ctx.agent.streamFn, openAIServiceTier);
}
const rawTextVerbosity = resolveAliasedParamValue(
[ctx.resolvedExtraParams, ctx.override],
"text_verbosity",
"textVerbosity",
);
if (rawTextVerbosity === null) {
log.debug("text verbosity suppressed by null override, skipping injection");
} else if (rawTextVerbosity !== undefined) {
const openAITextVerbosity = resolveOpenAITextVerbosity({
text_verbosity: rawTextVerbosity,
});
if (openAITextVerbosity) {
log.debug(
`applying OpenAI text verbosity=${openAITextVerbosity} for ${ctx.provider}/${ctx.modelId}`,
);
ctx.agent.streamFn = createOpenAITextVerbosityWrapper(
ctx.agent.streamFn,
openAITextVerbosity,
);
}
}
ctx.agent.streamFn = createCodexNativeWebSearchWrapper(ctx.agent.streamFn, {
config: ctx.cfg,
agentDir: ctx.agentDir,
});
}
// Work around upstream pi-ai hardcoding `store: false` for Responses API.
// Force `store=true` for direct OpenAI Responses models and auto-enable
// server-side compaction for compatible OpenAI Responses payloads.
ctx.agent.streamFn = createOpenAIResponsesContextManagementWrapper(
ctx.agent.streamFn,
ctx.effectiveExtraParams,
);
if (
ctx.provider === "openai" ||
ctx.provider === "openai-codex" ||
ctx.provider === "azure-openai" ||
ctx.provider === "azure-openai-responses"
) {
ctx.agent.streamFn = createOpenAIReasoningCompatibilityWrapper(ctx.agent.streamFn);
}
const rawParallelToolCalls = resolveAliasedParamValue(
[ctx.resolvedExtraParams, ctx.override],
"parallel_tool_calls",

View File

@@ -1,58 +0,0 @@
import type { AgentTool } from "@mariozechner/pi-agent-core";
import { describe, expect, it } from "vitest";
import { sanitizeToolsForGoogle } from "./google.js";
describe("sanitizeToolsForGoogle", () => {
const createTool = (parameters: Record<string, unknown>) =>
({
name: "test",
description: "test",
parameters,
execute: async () => ({ ok: true, content: [] }),
}) as unknown as AgentTool;
const createSchemaToolWithFormat = () =>
createTool({
type: "object",
additionalProperties: false,
properties: {
foo: {
type: "string",
format: "uuid",
},
},
});
const expectFormatRemoved = (
sanitized: AgentTool,
key: "additionalProperties" | "patternProperties",
) => {
const params = sanitized.parameters as {
additionalProperties?: unknown;
patternProperties?: unknown;
properties?: Record<string, { format?: unknown }>;
};
expect(params[key]).toBeUndefined();
expect(params.properties?.foo?.format).toBeUndefined();
};
it("strips unsupported schema keywords for Google providers", () => {
const tool = createSchemaToolWithFormat();
const [sanitized] = sanitizeToolsForGoogle({
tools: [tool],
provider: "google-gemini-cli",
});
expectFormatRemoved(sanitized, "additionalProperties");
});
it("returns original tools for non-google providers", () => {
const tool = createSchemaToolWithFormat();
const sanitized = sanitizeToolsForGoogle({
tools: [tool],
provider: "openai",
});
expect(sanitized).toEqual([tool]);
expect(sanitized[0]).toBe(tool);
});
});

View File

@@ -0,0 +1,615 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { SessionManager } from "@mariozechner/pi-coding-agent";
import type { OpenClawConfig } from "../../config/config.js";
import {
sanitizeProviderReplayHistoryWithPlugin,
validateProviderReplayTurnsWithPlugin,
} from "../../plugins/provider-runtime.js";
import type {
ProviderReplaySessionEntry,
ProviderReplaySessionState,
ProviderRuntimeModel,
} from "../../plugins/types.js";
import {
hasInterSessionUserProvenance,
normalizeInputProvenance,
} from "../../sessions/input-provenance.js";
import { resolveImageSanitizationLimits } from "../image-sanitization.js";
import {
downgradeOpenAIFunctionCallReasoningPairs,
downgradeOpenAIReasoningBlocks,
sanitizeGoogleTurnOrdering,
sanitizeSessionMessagesImages,
validateAnthropicTurns,
validateGeminiTurns,
} from "../pi-embedded-helpers.js";
import {
sanitizeToolCallInputs,
sanitizeToolUseResultPairing,
stripToolResultDetails,
} from "../session-transcript-repair.js";
import type { TranscriptPolicy } from "../transcript-policy.js";
import { resolveTranscriptPolicy } from "../transcript-policy.js";
import {
makeZeroUsageSnapshot,
normalizeUsage,
type AssistantUsageSnapshot,
type UsageLike,
} from "../usage.js";
import { log } from "./logger.js";
import { dropThinkingBlocks } from "./thinking.js";
const INTER_SESSION_PREFIX_BASE = "[Inter-session message]";
const MODEL_SNAPSHOT_CUSTOM_TYPE = "model-snapshot";
type AssistantHistoryMessage = Extract<AgentMessage, { role: "assistant" }>;
type RawAssistantHistoryMessage = Omit<AssistantHistoryMessage, "content"> & { content?: unknown };
type CustomEntryLike = { type?: unknown; customType?: unknown; data?: unknown };
type ModelSnapshotEntry = {
timestamp: number;
provider?: string;
modelApi?: string | null;
modelId?: string;
};
function buildInterSessionPrefix(message: AgentMessage): string {
const provenance = normalizeInputProvenance((message as { provenance?: unknown }).provenance);
if (!provenance) {
return INTER_SESSION_PREFIX_BASE;
}
const details = [
provenance.sourceSessionKey ? `sourceSession=${provenance.sourceSessionKey}` : undefined,
provenance.sourceChannel ? `sourceChannel=${provenance.sourceChannel}` : undefined,
provenance.sourceTool ? `sourceTool=${provenance.sourceTool}` : undefined,
].filter(Boolean);
if (details.length === 0) {
return INTER_SESSION_PREFIX_BASE;
}
return `${INTER_SESSION_PREFIX_BASE} ${details.join(" ")}`;
}
function annotateInterSessionUserMessages(messages: AgentMessage[]): AgentMessage[] {
let touched = false;
const out: AgentMessage[] = [];
for (const msg of messages) {
if (!hasInterSessionUserProvenance(msg as { role?: unknown; provenance?: unknown })) {
out.push(msg);
continue;
}
const prefix = buildInterSessionPrefix(msg);
const user = msg as Extract<AgentMessage, { role: "user" }>;
if (typeof user.content === "string") {
if (user.content.startsWith(prefix)) {
out.push(msg);
continue;
}
touched = true;
out.push({
...(msg as unknown as Record<string, unknown>),
content: `${prefix}\n${user.content}`,
} as AgentMessage);
continue;
}
if (!Array.isArray(user.content)) {
out.push(msg);
continue;
}
const textIndex = user.content.findIndex(
(block) =>
block &&
typeof block === "object" &&
(block as { type?: unknown }).type === "text" &&
typeof (block as { text?: unknown }).text === "string",
);
if (textIndex >= 0) {
const existing = user.content[textIndex] as { type: "text"; text: string };
if (existing.text.startsWith(prefix)) {
out.push(msg);
continue;
}
const nextContent = [...user.content];
nextContent[textIndex] = {
...existing,
text: `${prefix}\n${existing.text}`,
};
touched = true;
out.push({
...(msg as unknown as Record<string, unknown>),
content: nextContent,
} as AgentMessage);
continue;
}
touched = true;
out.push({
...(msg as unknown as Record<string, unknown>),
content: [{ type: "text", text: prefix }, ...user.content],
} as AgentMessage);
}
return touched ? out : messages;
}
function describeAssistantContentKind(content: unknown): string {
if (Array.isArray(content)) {
return "array";
}
if (content === null) {
return "null";
}
return typeof content;
}
function canonicalizeAssistantHistoryMessages(params: {
messages: AgentMessage[];
sessionId: string;
}): AgentMessage[] {
let touched = false;
let repairedCount = 0;
const repairedKinds = new Set<string>();
const out: AgentMessage[] = [];
for (const msg of params.messages) {
if (!msg || typeof msg !== "object" || msg.role !== "assistant") {
out.push(msg);
continue;
}
const assistant = msg as RawAssistantHistoryMessage;
if (Array.isArray(assistant.content)) {
out.push(msg);
continue;
}
// Session transcripts and custom stream boundaries have historically leaked
// malformed assistant payloads. Repair them here so Pi replay only sees the
// canonical array-based assistant content contract.
const repairedText = typeof assistant.content === "string" ? assistant.content : "";
out.push({
...(assistant as unknown as Record<string, unknown>),
content: [{ type: "text", text: repairedText }],
} as AgentMessage);
touched = true;
repairedCount += 1;
repairedKinds.add(describeAssistantContentKind(assistant.content));
}
if (!touched) {
return params.messages;
}
log.warn(
`sanitizeSessionHistory: canonicalized ${repairedCount} malformed assistant message(s) before replay ` +
`session=${params.sessionId} contentKinds=${Array.from(repairedKinds).join(",")}`,
);
return out;
}
function parseMessageTimestamp(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string") {
const parsed = Date.parse(value);
if (Number.isFinite(parsed)) {
return parsed;
}
}
return null;
}
function stripStaleAssistantUsageBeforeLatestCompaction(messages: AgentMessage[]): AgentMessage[] {
let latestCompactionSummaryIndex = -1;
let latestCompactionTimestamp: number | null = null;
for (let i = 0; i < messages.length; i += 1) {
const entry = messages[i];
if (entry?.role !== "compactionSummary") {
continue;
}
latestCompactionSummaryIndex = i;
latestCompactionTimestamp = parseMessageTimestamp(
(entry as { timestamp?: unknown }).timestamp ?? null,
);
}
if (latestCompactionSummaryIndex === -1) {
return messages;
}
const out = [...messages];
let touched = false;
for (let i = 0; i < out.length; i += 1) {
const candidate = out[i] as
| (AgentMessage & { usage?: unknown; timestamp?: unknown })
| undefined;
if (!candidate || candidate.role !== "assistant") {
continue;
}
if (!candidate.usage || typeof candidate.usage !== "object") {
continue;
}
const messageTimestamp = parseMessageTimestamp(candidate.timestamp);
const staleByTimestamp =
latestCompactionTimestamp !== null &&
messageTimestamp !== null &&
messageTimestamp <= latestCompactionTimestamp;
const staleByLegacyOrdering = i < latestCompactionSummaryIndex;
if (!staleByTimestamp && !staleByLegacyOrdering) {
continue;
}
// pi-coding-agent expects assistant usage to always be present during context
// accounting. Keep stale snapshots structurally valid, but zeroed out.
const candidateRecord = candidate as unknown as Record<string, unknown>;
out[i] = {
...candidateRecord,
usage: makeZeroUsageSnapshot(),
} as unknown as AgentMessage;
touched = true;
}
return touched ? out : messages;
}
function normalizeAssistantUsageSnapshot(usage: unknown) {
const normalized = normalizeUsage((usage ?? undefined) as UsageLike | undefined);
if (!normalized) {
return makeZeroUsageSnapshot();
}
const input = normalized.input ?? 0;
const output = normalized.output ?? 0;
const cacheRead = normalized.cacheRead ?? 0;
const cacheWrite = normalized.cacheWrite ?? 0;
const totalTokens = normalized.total ?? input + output + cacheRead + cacheWrite;
const cost = normalizeAssistantUsageCost(usage);
return {
input,
output,
cacheRead,
cacheWrite,
totalTokens,
...(cost ? { cost } : {}),
};
}
function normalizeAssistantUsageCost(usage: unknown): AssistantUsageSnapshot["cost"] | undefined {
const base = makeZeroUsageSnapshot().cost;
if (!usage || typeof usage !== "object") {
return undefined;
}
const rawCost = (usage as { cost?: unknown }).cost;
if (!rawCost || typeof rawCost !== "object") {
return undefined;
}
const cost = rawCost as Record<string, unknown>;
const inputRaw = toFiniteCostNumber(cost.input);
const outputRaw = toFiniteCostNumber(cost.output);
const cacheReadRaw = toFiniteCostNumber(cost.cacheRead);
const cacheWriteRaw = toFiniteCostNumber(cost.cacheWrite);
const totalRaw = toFiniteCostNumber(cost.total);
if (
inputRaw === undefined &&
outputRaw === undefined &&
cacheReadRaw === undefined &&
cacheWriteRaw === undefined &&
totalRaw === undefined
) {
return undefined;
}
const input = inputRaw ?? base.input;
const output = outputRaw ?? base.output;
const cacheRead = cacheReadRaw ?? base.cacheRead;
const cacheWrite = cacheWriteRaw ?? base.cacheWrite;
const total = totalRaw ?? input + output + cacheRead + cacheWrite;
return { input, output, cacheRead, cacheWrite, total };
}
function toFiniteCostNumber(value: unknown): number | undefined {
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
}
function ensureAssistantUsageSnapshots(messages: AgentMessage[]): AgentMessage[] {
if (messages.length === 0) {
return messages;
}
let touched = false;
const out = [...messages];
for (let i = 0; i < out.length; i += 1) {
const message = out[i] as (AgentMessage & { role?: unknown; usage?: unknown }) | undefined;
if (!message || message.role !== "assistant") {
continue;
}
const normalizedUsage = normalizeAssistantUsageSnapshot(message.usage);
const usageCost =
message.usage && typeof message.usage === "object"
? (message.usage as { cost?: unknown }).cost
: undefined;
const normalizedCost = normalizedUsage.cost;
if (
message.usage &&
typeof message.usage === "object" &&
(message.usage as { input?: unknown }).input === normalizedUsage.input &&
(message.usage as { output?: unknown }).output === normalizedUsage.output &&
(message.usage as { cacheRead?: unknown }).cacheRead === normalizedUsage.cacheRead &&
(message.usage as { cacheWrite?: unknown }).cacheWrite === normalizedUsage.cacheWrite &&
(message.usage as { totalTokens?: unknown }).totalTokens === normalizedUsage.totalTokens &&
((normalizedCost &&
usageCost &&
typeof usageCost === "object" &&
(usageCost as { input?: unknown }).input === normalizedCost.input &&
(usageCost as { output?: unknown }).output === normalizedCost.output &&
(usageCost as { cacheRead?: unknown }).cacheRead === normalizedCost.cacheRead &&
(usageCost as { cacheWrite?: unknown }).cacheWrite === normalizedCost.cacheWrite &&
(usageCost as { total?: unknown }).total === normalizedCost.total) ||
(!normalizedCost && usageCost === undefined))
) {
continue;
}
out[i] = {
...(message as unknown as Record<string, unknown>),
usage: normalizedUsage,
} as AgentMessage;
touched = true;
}
return touched ? out : messages;
}
function createProviderReplaySessionState(
sessionManager: SessionManager,
): ProviderReplaySessionState {
return {
getCustomEntries() {
try {
const customEntries: ProviderReplaySessionEntry[] = [];
for (const entry of sessionManager.getEntries()) {
const candidate = entry as CustomEntryLike;
if (candidate?.type !== "custom" || typeof candidate.customType !== "string") {
continue;
}
const customType = candidate.customType.trim();
if (!customType) {
continue;
}
customEntries.push({
customType,
data: candidate.data,
});
}
return customEntries;
} catch {
return [];
}
},
appendCustomEntry(customType: string, data: unknown) {
try {
sessionManager.appendCustomEntry(customType, data);
} catch {
// ignore persistence failures
}
},
};
}
function readLastModelSnapshot(sessionManager: SessionManager): ModelSnapshotEntry | null {
try {
const entries = sessionManager.getEntries();
for (let i = entries.length - 1; i >= 0; i -= 1) {
const entry = entries[i] as CustomEntryLike;
if (entry?.type !== "custom" || entry?.customType !== MODEL_SNAPSHOT_CUSTOM_TYPE) {
continue;
}
const data = entry?.data as ModelSnapshotEntry | undefined;
if (data && typeof data === "object") {
return data;
}
}
} catch {
return null;
}
return null;
}
function appendModelSnapshot(sessionManager: SessionManager, data: ModelSnapshotEntry): void {
try {
sessionManager.appendCustomEntry(MODEL_SNAPSHOT_CUSTOM_TYPE, data);
} catch {
// ignore persistence failures
}
}
function isSameModelSnapshot(a: ModelSnapshotEntry, b: ModelSnapshotEntry): boolean {
const normalize = (value?: string | null) => value ?? "";
return (
normalize(a.provider) === normalize(b.provider) &&
normalize(a.modelApi) === normalize(b.modelApi) &&
normalize(a.modelId) === normalize(b.modelId)
);
}
/**
* Applies the generic replay-history cleanup pipeline before provider-owned
* replay hooks run.
*/
export async function sanitizeSessionHistory(params: {
messages: AgentMessage[];
modelApi?: string | null;
modelId?: string;
provider?: string;
allowedToolNames?: Iterable<string>;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
model?: ProviderRuntimeModel;
sessionManager: SessionManager;
sessionId: string;
policy?: TranscriptPolicy;
}): Promise<AgentMessage[]> {
// Keep docs/reference/transcript-hygiene.md in sync with any logic changes here.
const policy =
params.policy ??
resolveTranscriptPolicy({
modelApi: params.modelApi,
provider: params.provider,
modelId: params.modelId,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
model: params.model,
});
const withInterSessionMarkers = annotateInterSessionUserMessages(params.messages);
const canonicalizedAssistantHistory = canonicalizeAssistantHistoryMessages({
messages: withInterSessionMarkers,
sessionId: params.sessionId,
});
const sanitizedImages = await sanitizeSessionMessagesImages(
canonicalizedAssistantHistory,
"session:history",
{
sanitizeMode: policy.sanitizeMode,
sanitizeToolCallIds: policy.sanitizeToolCallIds,
toolCallIdMode: policy.toolCallIdMode,
preserveSignatures: policy.preserveSignatures,
sanitizeThoughtSignatures: policy.sanitizeThoughtSignatures,
...resolveImageSanitizationLimits(params.config),
},
);
const droppedThinking = policy.dropThinkingBlocks
? dropThinkingBlocks(sanitizedImages)
: sanitizedImages;
const sanitizedToolCalls = sanitizeToolCallInputs(droppedThinking, {
allowedToolNames: params.allowedToolNames,
});
const repairedTools = policy.repairToolUseResultPairing
? sanitizeToolUseResultPairing(sanitizedToolCalls, {
erroredAssistantResultPolicy: "drop",
})
: sanitizedToolCalls;
const sanitizedToolResults = stripToolResultDetails(repairedTools);
const sanitizedCompactionUsage = ensureAssistantUsageSnapshots(
stripStaleAssistantUsageBeforeLatestCompaction(sanitizedToolResults),
);
const isOpenAIResponsesApi =
params.modelApi === "openai-responses" ||
params.modelApi === "openai-codex-responses" ||
params.modelApi === "azure-openai-responses";
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
const priorSnapshot = hasSnapshot ? readLastModelSnapshot(params.sessionManager) : null;
const modelChanged = priorSnapshot
? !isSameModelSnapshot(priorSnapshot, {
timestamp: 0,
provider: params.provider,
modelApi: params.modelApi,
modelId: params.modelId,
})
: false;
const sanitizedOpenAI = isOpenAIResponsesApi
? downgradeOpenAIFunctionCallReasoningPairs(
downgradeOpenAIReasoningBlocks(sanitizedCompactionUsage),
)
: sanitizedCompactionUsage;
const provider = params.provider?.trim();
const providerSanitized =
provider && provider.length > 0
? await sanitizeProviderReplayHistoryWithPlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId,
modelApi: params.modelApi,
model: params.model,
sessionId: params.sessionId,
messages: sanitizedOpenAI,
allowedToolNames: params.allowedToolNames,
sessionState: createProviderReplaySessionState(params.sessionManager),
},
})
: undefined;
const sanitizedWithProvider = providerSanitized ?? sanitizedOpenAI;
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
appendModelSnapshot(params.sessionManager, {
timestamp: Date.now(),
provider: params.provider,
modelApi: params.modelApi,
modelId: params.modelId,
});
}
if (!policy.applyGoogleTurnOrdering) {
return sanitizedWithProvider;
}
// Strict OpenAI-compatible providers (vLLM, Gemma, etc.) also reject
// conversations that start with an assistant turn (e.g. delivery-mirror
// messages after /new). Provider hooks may already have applied a
// provider-owned ordering rewrite above; keep this generic fallback for the
// strict OpenAI-compatible path and for any provider that leaves assistant-
// first repair to core. See #38962.
return sanitizeGoogleTurnOrdering(sanitizedWithProvider);
}
/**
* Runs provider-owned replay validation before falling back to the remaining
* generic validator pipeline.
*/
export async function validateReplayTurns(params: {
messages: AgentMessage[];
modelApi?: string | null;
modelId?: string;
provider?: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
model?: ProviderRuntimeModel;
sessionId?: string;
policy?: TranscriptPolicy;
}): Promise<AgentMessage[]> {
const policy =
params.policy ??
resolveTranscriptPolicy({
modelApi: params.modelApi,
provider: params.provider,
modelId: params.modelId,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
model: params.model,
});
const provider = params.provider?.trim();
if (provider) {
const providerValidated = await validateProviderReplayTurnsWithPlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId,
modelApi: params.modelApi,
model: params.model,
sessionId: params.sessionId,
messages: params.messages,
},
});
if (providerValidated) {
return providerValidated;
}
}
const validatedGemini = policy.validateGeminiTurns
? validateGeminiTurns(params.messages)
: params.messages;
return policy.validateAnthropicTurns ? validateAnthropicTurns(validatedGemini) : validatedGemini;
}

View File

@@ -13,8 +13,16 @@ import type {
IngestResult,
} from "../../../context-engine/types.js";
import type { EmbeddedContextFile } from "../../pi-embedded-helpers.js";
import type { MessagingToolSend } from "../../pi-embedded-messaging.js";
import type { WorkspaceBootstrapFile } from "../../workspace.js";
type SubscribeEmbeddedPiSessionFn =
typeof import("../../pi-embedded-subscribe.js").subscribeEmbeddedPiSession;
type AcquireSessionWriteLockFn =
typeof import("../../session-write-lock.js").acquireSessionWriteLock;
type SubscriptionMock = ReturnType<SubscribeEmbeddedPiSessionFn>;
const hoisted = vi.hoisted(() => {
type BootstrapContext = {
bootstrapFiles: WorkspaceBootstrapFile[];
@@ -24,11 +32,32 @@ const hoisted = vi.hoisted(() => {
const createAgentSessionMock = vi.fn();
const sessionManagerOpenMock = vi.fn();
const resolveSandboxContextMock = vi.fn();
const subscribeEmbeddedPiSessionMock = vi.fn();
const acquireSessionWriteLockMock = vi.fn();
const installToolResultContextGuardMock = vi.fn(() => () => {});
const flushPendingToolResultsAfterIdleMock = vi.fn(async () => {});
const releaseWsSessionMock = vi.fn(() => {});
const subscribeEmbeddedPiSessionMock = vi.fn<SubscribeEmbeddedPiSessionFn>(
(_params) =>
({
assistantTexts: [] as string[],
toolMetas: [] as Array<{ toolName: string; meta?: string }>,
unsubscribe: () => {},
waitForCompactionRetry: async () => {},
getMessagingToolSentTexts: () => [] as string[],
getMessagingToolSentMediaUrls: () => [] as string[],
getMessagingToolSentTargets: () => [] as MessagingToolSend[],
getSuccessfulCronAdds: () => 0,
didSendViaMessagingTool: () => false,
didSendDeterministicApprovalPrompt: () => false,
getLastToolError: () => undefined,
getUsageTotals: () => undefined,
getCompactionCount: () => 0,
isCompacting: () => false,
isCompactionInFlight: () => false,
}) satisfies SubscriptionMock,
);
const acquireSessionWriteLockMock = vi.fn<AcquireSessionWriteLockFn>(async (_params) => ({
release: async () => {},
}));
const resolveBootstrapContextForRunMock = vi.fn<() => Promise<BootstrapContext>>(async () => ({
bootstrapFiles: [],
contextFiles: [],
@@ -97,8 +126,8 @@ vi.mock("../../session-tool-result-guard-wrapper.js", () => ({
}));
vi.mock("../../pi-embedded-subscribe.js", () => ({
subscribeEmbeddedPiSession: (...args: unknown[]) =>
hoisted.subscribeEmbeddedPiSessionMock(...args),
subscribeEmbeddedPiSession: (params: Parameters<SubscribeEmbeddedPiSessionFn>[0]) =>
hoisted.subscribeEmbeddedPiSessionMock(params),
}));
vi.mock("../../../plugins/hook-runner-global.js", () => ({
@@ -153,13 +182,16 @@ vi.mock("../extensions.js", () => ({
buildEmbeddedExtensionFactories: () => [],
}));
vi.mock("../google.js", () => ({
logToolSchemasForGoogle: () => {},
vi.mock("../replay-history.js", () => ({
sanitizeSessionHistory: async ({ messages }: { messages: unknown[] }) => messages,
sanitizeToolsForGoogle: ({ tools }: { tools: unknown[] }) => tools,
validateReplayTurns: async ({ messages }: { messages: unknown[] }) => messages,
}));
vi.mock("../tool-schema-runtime.js", () => ({
logProviderToolSchemaDiagnostics: () => {},
normalizeProviderToolSchemas: ({ tools }: { tools: unknown[] }) => tools,
}));
vi.mock("../../session-file-repair.js", () => ({
repairSessionFileIfNeeded: async () => {},
}));
@@ -174,8 +206,8 @@ vi.mock("../session-manager-init.js", () => ({
}));
vi.mock("../../session-write-lock.js", () => ({
acquireSessionWriteLock: (...args: unknown[]) =>
hoisted.acquireSessionWriteLockMock.apply(undefined, args),
acquireSessionWriteLock: (params: Parameters<AcquireSessionWriteLockFn>[0]) =>
hoisted.acquireSessionWriteLockMock(params),
resolveSessionLockMaxHoldFromTimeout: () => 1,
}));
@@ -218,12 +250,14 @@ vi.mock("../system-prompt.js", () => ({
createSystemPromptOverride: (prompt: string) => () => prompt,
}));
vi.mock("../extra-params.js", () => ({
applyExtraParamsToAgent: () => ({
effectiveExtraParams: {},
}),
resolveAgentTransportOverride: () => undefined,
}));
vi.mock("../extra-params.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../extra-params.js")>();
return {
...actual,
applyExtraParamsToAgent: () => ({ effectiveExtraParams: {} }),
resolveAgentTransportOverride: () => undefined,
};
});
vi.mock("../../openai-ws-stream.js", () => ({
createOpenAIWebSocketStreamFn: vi.fn(),
@@ -461,7 +495,7 @@ export type MutableSession = {
steer: (text: string) => Promise<void>;
};
export function createSubscriptionMock() {
export function createSubscriptionMock(): SubscriptionMock {
return {
assistantTexts: [] as string[],
toolMetas: [] as Array<{ toolName: string; meta?: string }>,
@@ -469,7 +503,7 @@ export function createSubscriptionMock() {
waitForCompactionRetry: async () => {},
getMessagingToolSentTexts: () => [] as string[],
getMessagingToolSentMediaUrls: () => [] as string[],
getMessagingToolSentTargets: () => [] as unknown[],
getMessagingToolSentTargets: () => [] as MessagingToolSend[],
getSuccessfulCronAdds: () => 0,
didSendViaMessagingTool: () => false,
didSendDeterministicApprovalPrompt: () => false,
@@ -477,13 +511,16 @@ export function createSubscriptionMock() {
getUsageTotals: () => undefined,
getCompactionCount: () => 0,
isCompacting: () => false,
isCompactionInFlight: () => false,
};
}
export function resetEmbeddedAttemptHarness(
params: {
includeSpawnSubagent?: boolean;
subscribeImpl?: () => ReturnType<typeof createSubscriptionMock>;
subscribeImpl?: Parameters<
(typeof hoisted.subscribeEmbeddedPiSessionMock)["mockImplementation"]
>[0];
sessionMessages?: AgentMessage[];
} = {},
) {

View File

@@ -11,11 +11,12 @@ import { buildAgentSystemPrompt } from "../../system-prompt.js";
import {
buildAfterTurnRuntimeContext,
composeSystemPromptWithHookContext,
decodeHtmlEntitiesInObject,
prependSystemPromptAddition,
resolveAttemptFsWorkspaceOnly,
resolveEmbeddedAgentStreamFn,
resolvePromptBuildHookResult,
resolvePromptModeForSession,
decodeHtmlEntitiesInObject,
wrapStreamFnRepairMalformedToolCallArguments,
wrapStreamFnSanitizeMalformedToolCalls,
wrapStreamFnTrimToolCallNames,
@@ -221,6 +222,33 @@ describe("resolvePromptModeForSession", () => {
});
});
describe("resolveEmbeddedAgentStreamFn", () => {
it("injects authStorage api keys into provider-owned stream functions", async () => {
const providerStreamFn = vi.fn(async (_model, _context, options) => options);
const streamFn = resolveEmbeddedAgentStreamFn({
currentStreamFn: undefined,
providerStreamFn,
shouldUseWebSocketTransport: false,
sessionId: "session-1",
model: {
api: "openai-completions",
provider: "demo-provider",
id: "demo-model",
} as never,
authStorage: {
getApiKey: vi.fn(async () => "demo-runtime-key"),
},
});
await expect(
streamFn({ provider: "demo-provider", id: "demo-model" } as never, {} as never, {}),
).resolves.toMatchObject({
apiKey: "demo-runtime-key",
});
expect(providerStreamFn).toHaveBeenCalledTimes(1);
});
});
describe("resolveAttemptFsWorkspaceOnly", () => {
it("uses global tools.fs.workspaceOnly when agent has no override", () => {
const cfg: OpenClawConfig = {

View File

@@ -101,15 +101,10 @@ import { resolveCompactionTimeoutMs } from "../compaction-safety-timeout.js";
import { runContextEngineMaintenance } from "../context-engine-maintenance.js";
import { buildEmbeddedExtensionFactories } from "../extensions.js";
import { applyExtraParamsToAgent, resolveAgentTransportOverride } from "../extra-params.js";
import {
logToolSchemasForGoogle,
sanitizeSessionHistory,
sanitizeToolsForGoogle,
validateReplayTurns,
} from "../google.js";
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js";
import { log } from "../logger.js";
import { buildEmbeddedMessageActionDiscoveryInput } from "../message-action-discovery-input.js";
import { sanitizeSessionHistory, validateReplayTurns } from "../replay-history.js";
import {
clearActiveEmbeddedRun,
type EmbeddedPiQueueHandle,
@@ -125,13 +120,13 @@ import {
buildEmbeddedSystemPrompt,
createSystemPromptOverride,
} from "../system-prompt.js";
import {
dropThinkingBlocks,
sanitizeThinkingForRecovery,
wrapAnthropicStreamWithRecovery,
} from "../thinking.js";
import { dropThinkingBlocks } from "../thinking.js";
import { collectAllowedToolNames } from "../tool-name-allowlist.js";
import { installToolResultContextGuard } from "../tool-result-context-guard.js";
import {
logProviderToolSchemaDiagnostics,
normalizeProviderToolSchemas,
} from "../tool-schema-runtime.js";
import { splitSdkTools } from "../tool-split.js";
import { describeUnknownError, mapThinkingLevel } from "../utils.js";
import { flushPendingToolResultsAfterIdle } from "../wait-for-idle-before-flush.js";
@@ -220,10 +215,6 @@ export {
const MAX_BTW_SNAPSHOT_MESSAGES = 100;
function shouldEnableAnthropicThinkingRecovery(api: string | null | undefined): boolean {
return api === "anthropic-messages" || api === "bedrock-converse-stream";
}
export function resolveEmbeddedAgentStreamFn(params: {
currentStreamFn: StreamFn | undefined;
providerStreamFn?: StreamFn;
@@ -236,10 +227,9 @@ export function resolveEmbeddedAgentStreamFn(params: {
}): StreamFn {
if (params.providerStreamFn) {
const inner = params.providerStreamFn;
// The default pi-coding-agent streamFn injects apiKey from authStorage
// into options via modelRegistry.getApiKeyAndHeaders(). Provider-supplied
// stream functions bypass that default, so we replicate the injection here
// so the resolved credential reaches the provider's HTTP layer.
// Provider-owned transports bypass pi-coding-agent's default auth lookup,
// so keep injecting the resolved runtime apiKey for streamSimple-compatible
// transports that still read credentials from options.apiKey.
if (params.authStorage) {
const { authStorage, model } = params;
return async (m, context, options) => {
@@ -505,7 +495,7 @@ export async function runEmbeddedAttempt(
return allTools;
})();
const toolsEnabled = supportsModelTools(params.model);
const tools = sanitizeToolsForGoogle({
const tools = normalizeProviderToolSchemas({
tools: toolsEnabled ? toolsRaw : [],
provider: params.provider,
config: params.config,
@@ -553,7 +543,7 @@ export async function runEmbeddedAttempt(
tools: effectiveTools,
clientTools,
});
logToolSchemasForGoogle({
logProviderToolSchemaDiagnostics({
tools: effectiveTools,
provider: params.provider,
config: params.config,
@@ -1114,13 +1104,6 @@ export async function runEmbeddedAttempt(
);
}
if (shouldEnableAnthropicThinkingRecovery(params.model.api)) {
activeSession.agent.streamFn = wrapAnthropicStreamWithRecovery(
activeSession.agent.streamFn,
{ id: activeSession.sessionId },
);
}
if (anthropicPayloadLogger) {
activeSession.agent.streamFn = anthropicPayloadLogger.wrapStreamFn(
activeSession.agent.streamFn,
@@ -1146,28 +1129,6 @@ export async function runEmbeddedAttempt(
}
try {
if (shouldEnableAnthropicThinkingRecovery(params.model.api)) {
const originalMessageCount = activeSession.messages.length;
const { messages, prefill } = sanitizeThinkingForRecovery(activeSession.messages);
if (messages !== activeSession.messages) {
activeSession.agent.replaceMessages(messages);
}
if (messages.length !== originalMessageCount) {
log.warn(
`[session-recovery] dropped latest assistant message with incomplete thinking: sessionId=${params.sessionId}`,
);
}
if (prefill) {
// Keeping the signed-thinking turn intact is a forward-compatibility
// signal for future prefill-style recovery; the current fallback
// still comes from the one-shot stream wrapper if Anthropic rejects
// the replayed payload.
log.warn(
`[session-recovery] keeping latest assistant message with signed thinking and incomplete text: sessionId=${params.sessionId}`,
);
}
}
const prior = await sanitizeSessionHistory({
messages: activeSession.messages,
modelApi: params.model.api,

View File

@@ -3,7 +3,7 @@ import type { ToolResultMessage, UserMessage } from "@mariozechner/pi-ai";
import { SessionManager } from "@mariozechner/pi-coding-agent";
import { describe, expect, it } from "vitest";
import { makeAgentAssistantMessage } from "../test-helpers/agent-message-fixtures.js";
import { sanitizeSessionHistory } from "./google.js";
import { sanitizeSessionHistory } from "./replay-history.js";
describe("sanitizeSessionHistory toolResult details stripping", () => {
it("strips toolResult.details so untrusted payloads are not fed back to the model", async () => {

View File

@@ -0,0 +1,92 @@
import type { AgentTool } from "@mariozechner/pi-agent-core";
import type { TSchema } from "@sinclair/typebox";
import type { OpenClawConfig } from "../../config/config.js";
import {
inspectProviderToolSchemasWithPlugin,
normalizeProviderToolSchemasWithPlugin,
} from "../../plugins/provider-runtime.js";
import type { ProviderRuntimeModel } from "../../plugins/types.js";
import type { AnyAgentTool } from "../tools/common.js";
import { log } from "./logger.js";
type ProviderToolSchemaParams<TSchemaType extends TSchema = TSchema, TResult = unknown> = {
tools: AgentTool<TSchemaType, TResult>[];
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
modelId?: string;
modelApi?: string | null;
model?: ProviderRuntimeModel;
};
/**
* Runs provider-owned tool-schema normalization without encoding provider
* families in the embedded runner.
*/
export function normalizeProviderToolSchemas<
TSchemaType extends TSchema = TSchema,
TResult = unknown,
>(params: ProviderToolSchemaParams<TSchemaType, TResult>): AgentTool<TSchemaType, TResult>[] {
const provider = params.provider.trim();
const pluginNormalized = normalizeProviderToolSchemasWithPlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId,
modelApi: params.modelApi,
model: params.model,
tools: params.tools as unknown as AnyAgentTool[],
},
});
return Array.isArray(pluginNormalized)
? (pluginNormalized as AgentTool<TSchemaType, TResult>[])
: params.tools;
}
/**
* Logs provider-owned tool-schema diagnostics after normalization.
*/
export function logProviderToolSchemaDiagnostics(params: ProviderToolSchemaParams): void {
const provider = params.provider.trim();
const diagnostics = inspectProviderToolSchemasWithPlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId,
modelApi: params.modelApi,
model: params.model,
tools: params.tools as unknown as AnyAgentTool[],
},
});
if (!Array.isArray(diagnostics)) {
return;
}
log.info("provider tool schema snapshot", {
provider: params.provider,
toolCount: params.tools.length,
tools: params.tools.map((tool, index) => `${index}:${tool.name}`),
});
for (const diagnostic of diagnostics) {
log.warn("provider tool schema diagnostic", {
provider: params.provider,
index: diagnostic.toolIndex,
tool: diagnostic.toolName,
violations: diagnostic.violations.slice(0, 12),
violationCount: diagnostic.violations.length,
});
}
}

View File

@@ -5,11 +5,14 @@ import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
import { Type } from "@sinclair/typebox";
import { describe, expect, it, vi } from "vitest";
import { createBrowserTool } from "../plugin-sdk/browser.js";
import { XAI_UNSUPPORTED_SCHEMA_KEYWORDS } from "../plugin-sdk/provider-tools.js";
import {
findUnsupportedSchemaKeywords,
GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS,
XAI_UNSUPPORTED_SCHEMA_KEYWORDS,
} from "../plugin-sdk/provider-tools.js";
import { applyXaiModelCompat } from "../plugin-sdk/xai.js";
import "./test-helpers/fast-coding-tools.js";
import { createOpenClawTools } from "./openclaw-tools.js";
import { findUnsupportedSchemaKeywords } from "./pi-embedded-runner/google.js";
import { __testing, createOpenClawCodingTools } from "./pi-tools.js";
import { createOpenClawReadTool, createSandboxedReadTool } from "./pi-tools.read.js";
import { createHostSandboxFsBridge } from "./test-helpers/host-sandbox-fs-bridge.js";
@@ -447,7 +450,11 @@ describe("createOpenClawCodingTools", () => {
senderIsOwner: true,
});
for (const tool of googleTools) {
const violations = findUnsupportedSchemaKeywords(tool.parameters, `${tool.name}.parameters`);
const violations = findUnsupportedSchemaKeywords(
tool.parameters,
`${tool.name}.parameters`,
GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS,
);
expect(violations).toEqual([]);
}
});
@@ -460,7 +467,11 @@ describe("createOpenClawCodingTools", () => {
expect(xaiTools.some((tool) => tool.name === "web_search")).toBe(true);
for (const tool of xaiTools) {
const violations = findUnsupportedSchemaKeywords(tool.parameters, `${tool.name}.parameters`);
const violations = findUnsupportedSchemaKeywords(
tool.parameters,
`${tool.name}.parameters`,
XAI_UNSUPPORTED_SCHEMA_KEYWORDS,
);
expect(
violations.filter((violation) => {
const keyword = violation.split(".").at(-1) ?? "";

View File

@@ -1,266 +0,0 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const resolveProviderCapabilitiesWithPluginMock = vi.fn((params: { provider: string }) => {
switch (params.provider) {
case "anthropic":
return {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
};
case "anthropic-vertex":
return {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
};
case "amazon-bedrock":
return {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
};
case "openai":
return {
providerFamily: "openai",
};
case "openrouter":
return {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
};
case "openai-codex":
return {
providerFamily: "openai",
};
case "github-copilot":
return {
dropThinkingBlockModelHints: ["claude"],
};
case "kilocode":
return {
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
};
case "kimi":
return {
openAiPayloadNormalizationMode: "moonshot-thinking",
preserveAnthropicThinkingSignatures: false,
};
default:
return undefined;
}
});
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderCapabilitiesWithPlugin: (params: { provider: string }) =>
resolveProviderCapabilitiesWithPluginMock(params),
}));
let isAnthropicProviderFamily: typeof import("./provider-capabilities.js").isAnthropicProviderFamily;
let isOpenAiProviderFamily: typeof import("./provider-capabilities.js").isOpenAiProviderFamily;
let requiresOpenAiCompatibleAnthropicToolPayload: typeof import("./provider-capabilities.js").requiresOpenAiCompatibleAnthropicToolPayload;
let resolveProviderCapabilities: typeof import("./provider-capabilities.js").resolveProviderCapabilities;
let resolveTranscriptToolCallIdMode: typeof import("./provider-capabilities.js").resolveTranscriptToolCallIdMode;
let shouldDropThinkingBlocksForModel: typeof import("./provider-capabilities.js").shouldDropThinkingBlocksForModel;
let shouldSanitizeGeminiThoughtSignaturesForModel: typeof import("./provider-capabilities.js").shouldSanitizeGeminiThoughtSignaturesForModel;
let supportsOpenAiCompatTurnValidation: typeof import("./provider-capabilities.js").supportsOpenAiCompatTurnValidation;
let usesMoonshotThinkingPayloadCompat: typeof import("./provider-capabilities.js").usesMoonshotThinkingPayloadCompat;
let providerCapabilityTesting: typeof import("./provider-capabilities.js").__testing;
describe("resolveProviderCapabilities", () => {
beforeAll(async () => {
({
isAnthropicProviderFamily,
isOpenAiProviderFamily,
requiresOpenAiCompatibleAnthropicToolPayload,
resolveProviderCapabilities,
resolveTranscriptToolCallIdMode,
shouldDropThinkingBlocksForModel,
shouldSanitizeGeminiThoughtSignaturesForModel,
supportsOpenAiCompatTurnValidation,
usesMoonshotThinkingPayloadCompat,
__testing: providerCapabilityTesting,
} = await import("./provider-capabilities.js"));
});
beforeEach(() => {
resolveProviderCapabilitiesWithPluginMock.mockClear();
providerCapabilityTesting.resetDepsForTests();
});
it("returns provider-owned anthropic defaults for ordinary providers", () => {
expect(resolveProviderCapabilities("anthropic")).toEqual({
anthropicToolSchemaMode: "native",
anthropicToolChoiceMode: "native",
openAiPayloadNormalizationMode: "default",
providerFamily: "anthropic",
preserveAnthropicThinkingSignatures: true,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: ["claude"],
});
expect(resolveProviderCapabilities("anthropic-vertex")).toEqual({
anthropicToolSchemaMode: "native",
anthropicToolChoiceMode: "native",
openAiPayloadNormalizationMode: "default",
providerFamily: "anthropic",
preserveAnthropicThinkingSignatures: true,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: ["claude"],
});
expect(resolveProviderCapabilities("amazon-bedrock")).toEqual({
anthropicToolSchemaMode: "native",
anthropicToolChoiceMode: "native",
openAiPayloadNormalizationMode: "default",
providerFamily: "anthropic",
preserveAnthropicThinkingSignatures: true,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: ["claude"],
});
});
it("preserves built-in fallback capability hints when plugin overrides are partial", () => {
resolveProviderCapabilitiesWithPluginMock.mockImplementationOnce(() => ({
providerFamily: "anthropic",
}));
expect(resolveProviderCapabilities("anthropic")).toEqual({
anthropicToolSchemaMode: "native",
anthropicToolChoiceMode: "native",
openAiPayloadNormalizationMode: "default",
providerFamily: "anthropic",
preserveAnthropicThinkingSignatures: true,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: ["claude"],
});
});
it("normalizes kimi aliases to the same capability set", () => {
expect(resolveProviderCapabilities("kimi")).toEqual(resolveProviderCapabilities("kimi-code"));
expect(resolveProviderCapabilities("kimi-code")).toEqual({
anthropicToolSchemaMode: "openai-functions",
anthropicToolChoiceMode: "openai-string-modes",
openAiPayloadNormalizationMode: "moonshot-thinking",
providerFamily: "default",
preserveAnthropicThinkingSignatures: false,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: [],
});
});
it("flags providers that opt out of OpenAI-compatible turn validation", () => {
expect(supportsOpenAiCompatTurnValidation("openrouter")).toBe(false);
expect(supportsOpenAiCompatTurnValidation("opencode")).toBe(false);
expect(supportsOpenAiCompatTurnValidation("opencode-go")).toBe(false);
expect(supportsOpenAiCompatTurnValidation("moonshot")).toBe(true);
});
it("routes moonshot payload compatibility through the capability registry", () => {
expect(usesMoonshotThinkingPayloadCompat("moonshot")).toBe(true);
expect(usesMoonshotThinkingPayloadCompat("kimi-coding")).toBe(true);
expect(usesMoonshotThinkingPayloadCompat("openai")).toBe(false);
});
it("keeps the normalized kimi fallback aligned when plugin capabilities are unavailable", () => {
resolveProviderCapabilitiesWithPluginMock.mockImplementationOnce(() => undefined);
expect(usesMoonshotThinkingPayloadCompat("kimi-coding")).toBe(true);
});
it("resolves transcript thought-signature and tool-call quirks through the registry", () => {
expect(
shouldSanitizeGeminiThoughtSignaturesForModel({
provider: "openrouter",
modelId: "google/gemini-2.5-pro-preview",
}),
).toBe(true);
expect(
shouldSanitizeGeminiThoughtSignaturesForModel({
provider: "kilocode",
modelId: "gemini-2.0-flash",
}),
).toBe(true);
expect(
shouldSanitizeGeminiThoughtSignaturesForModel({
provider: "opencode-go",
modelId: "google/gemini-2.5-pro-preview",
}),
).toBe(true);
expect(resolveTranscriptToolCallIdMode("mistral", "mistral-large-latest")).toBe("strict9");
});
it("treats kimi aliases as OpenAI-style anthropic tool payload providers", () => {
expect(requiresOpenAiCompatibleAnthropicToolPayload("kimi")).toBe(true);
expect(requiresOpenAiCompatibleAnthropicToolPayload("kimi-code")).toBe(true);
expect(requiresOpenAiCompatibleAnthropicToolPayload("kimi-coding")).toBe(true);
expect(requiresOpenAiCompatibleAnthropicToolPayload("anthropic")).toBe(false);
});
it("tracks provider families and model-specific transcript quirks in the registry", () => {
expect(isOpenAiProviderFamily("openai")).toBe(true);
expect(isAnthropicProviderFamily("anthropic-vertex")).toBe(true);
expect(isAnthropicProviderFamily("amazon-bedrock")).toBe(true);
expect(
shouldDropThinkingBlocksForModel({
provider: "anthropic",
modelId: "claude-opus-4-6",
}),
).toBe(true);
expect(
shouldDropThinkingBlocksForModel({
provider: "anthropic-vertex",
modelId: "claude-sonnet-4-6",
}),
).toBe(true);
expect(
shouldDropThinkingBlocksForModel({
provider: "amazon-bedrock",
modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
}),
).toBe(true);
expect(
shouldDropThinkingBlocksForModel({
provider: "github-copilot",
modelId: "claude-3.7-sonnet",
}),
).toBe(true);
});
it("forwards config and workspace context to plugin capability lookup", () => {
const config = { plugins: { enabled: true } };
const env = { OPENCLAW_HOME: "/tmp/openclaw-home" } as NodeJS.ProcessEnv;
const lookup = vi.fn(() => undefined);
providerCapabilityTesting.setResolveProviderCapabilitiesWithPluginForTest(lookup);
resolveProviderCapabilities("anthropic", {
config,
workspaceDir: "/tmp/workspace",
env,
});
expect(lookup).toHaveBeenLastCalledWith({
provider: "anthropic",
config,
workspaceDir: "/tmp/workspace",
env,
});
});
});

View File

@@ -1,237 +0,0 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveProviderCapabilitiesWithPlugin as resolveProviderCapabilitiesWithPluginRuntime } from "../plugins/provider-runtime.js";
import { normalizeProviderId } from "./provider-id.js";
export type ProviderCapabilities = {
anthropicToolSchemaMode: "native" | "openai-functions";
anthropicToolChoiceMode: "native" | "openai-string-modes";
openAiPayloadNormalizationMode: "default" | "moonshot-thinking";
providerFamily: "default" | "openai" | "anthropic";
preserveAnthropicThinkingSignatures: boolean;
openAiCompatTurnValidation: boolean;
geminiThoughtSignatureSanitization: boolean;
transcriptToolCallIdMode: "default" | "strict9";
transcriptToolCallIdModelHints: string[];
geminiThoughtSignatureModelHints: string[];
dropThinkingBlockModelHints: string[];
};
export type ProviderCapabilityLookupOptions = {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
};
const DEFAULT_PROVIDER_CAPABILITIES: ProviderCapabilities = {
anthropicToolSchemaMode: "native",
anthropicToolChoiceMode: "native",
openAiPayloadNormalizationMode: "default",
providerFamily: "default",
preserveAnthropicThinkingSignatures: true,
openAiCompatTurnValidation: true,
geminiThoughtSignatureSanitization: false,
transcriptToolCallIdMode: "default",
transcriptToolCallIdModelHints: [],
geminiThoughtSignatureModelHints: [],
dropThinkingBlockModelHints: [],
};
const PLUGIN_CAPABILITIES_FALLBACKS: Record<string, Partial<ProviderCapabilities>> = {
anthropic: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
mistral: {
transcriptToolCallIdMode: "strict9",
transcriptToolCallIdModelHints: [
"mistral",
"mixtral",
"codestral",
"pixtral",
"devstral",
"ministral",
"mistralai",
],
},
moonshot: {
openAiPayloadNormalizationMode: "moonshot-thinking",
},
kimi: {
anthropicToolSchemaMode: "openai-functions",
anthropicToolChoiceMode: "openai-string-modes",
openAiPayloadNormalizationMode: "moonshot-thinking",
},
opencode: {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
"opencode-go": {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
},
openai: {
providerFamily: "openai",
},
};
const defaultResolveProviderCapabilitiesWithPlugin = resolveProviderCapabilitiesWithPluginRuntime;
const providerCapabilityDeps = {
resolveProviderCapabilitiesWithPlugin: defaultResolveProviderCapabilitiesWithPlugin,
};
export const __testing = {
setResolveProviderCapabilitiesWithPluginForTest(
resolveProviderCapabilitiesWithPlugin?: typeof defaultResolveProviderCapabilitiesWithPlugin,
): void {
providerCapabilityDeps.resolveProviderCapabilitiesWithPlugin =
resolveProviderCapabilitiesWithPlugin ?? defaultResolveProviderCapabilitiesWithPlugin;
},
resetDepsForTests(): void {
providerCapabilityDeps.resolveProviderCapabilitiesWithPlugin =
defaultResolveProviderCapabilitiesWithPlugin;
},
};
export function resolveProviderCapabilities(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): ProviderCapabilities {
const normalized = normalizeProviderId(provider ?? "");
const pluginCapabilities = normalized
? providerCapabilityDeps.resolveProviderCapabilitiesWithPlugin({
provider: normalized,
config: options?.config,
workspaceDir: options?.workspaceDir,
env: options?.env,
})
: undefined;
return {
...DEFAULT_PROVIDER_CAPABILITIES,
...PLUGIN_CAPABILITIES_FALLBACKS[normalized],
...pluginCapabilities,
};
}
export function preservesAnthropicThinkingSignatures(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return resolveProviderCapabilities(provider, options).preserveAnthropicThinkingSignatures;
}
export function requiresOpenAiCompatibleAnthropicToolPayload(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
const capabilities = resolveProviderCapabilities(provider, options);
return (
capabilities.anthropicToolSchemaMode !== "native" ||
capabilities.anthropicToolChoiceMode !== "native"
);
}
export function usesOpenAiFunctionAnthropicToolSchema(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return (
resolveProviderCapabilities(provider, options).anthropicToolSchemaMode === "openai-functions"
);
}
export function usesOpenAiStringModeAnthropicToolChoice(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return (
resolveProviderCapabilities(provider, options).anthropicToolChoiceMode === "openai-string-modes"
);
}
export function supportsOpenAiCompatTurnValidation(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return resolveProviderCapabilities(provider, options).openAiCompatTurnValidation;
}
export function usesMoonshotThinkingPayloadCompat(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return (
resolveProviderCapabilities(provider, options).openAiPayloadNormalizationMode ===
"moonshot-thinking"
);
}
export function sanitizesGeminiThoughtSignatures(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return resolveProviderCapabilities(provider, options).geminiThoughtSignatureSanitization;
}
function modelIncludesAnyHint(modelId: string | null | undefined, hints: string[]): boolean {
const normalized = (modelId ?? "").toLowerCase();
return Boolean(normalized) && hints.some((hint) => normalized.includes(hint));
}
export function isOpenAiProviderFamily(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return resolveProviderCapabilities(provider, options).providerFamily === "openai";
}
export function isAnthropicProviderFamily(
provider?: string | null,
options?: ProviderCapabilityLookupOptions,
): boolean {
return resolveProviderCapabilities(provider, options).providerFamily === "anthropic";
}
export function shouldDropThinkingBlocksForModel(params: {
provider?: string | null;
modelId?: string | null;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): boolean {
return modelIncludesAnyHint(
params.modelId,
resolveProviderCapabilities(params.provider, params).dropThinkingBlockModelHints,
);
}
export function shouldSanitizeGeminiThoughtSignaturesForModel(params: {
provider?: string | null;
modelId?: string | null;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): boolean {
const capabilities = resolveProviderCapabilities(params.provider, params);
return (
capabilities.geminiThoughtSignatureSanitization &&
modelIncludesAnyHint(params.modelId, capabilities.geminiThoughtSignatureModelHints)
);
}
export function resolveTranscriptToolCallIdMode(
provider?: string | null,
modelId?: string | null,
options?: ProviderCapabilityLookupOptions,
): "strict9" | undefined {
const capabilities = resolveProviderCapabilities(provider, options);
const mode = capabilities.transcriptToolCallIdMode;
if (mode === "strict9") {
return mode;
}
if (modelIncludesAnyHint(modelId, capabilities.transcriptToolCallIdModelHints)) {
return "strict9";
}
return undefined;
}

View File

@@ -1,30 +1,108 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderCapabilitiesWithPlugin: vi.fn(({ provider }: { provider?: string }) => {
switch (provider) {
case "kimi":
case "kimi-code":
return {
providerFamily: "anthropic",
preserveAnthropicThinkingSignatures: false,
};
case "openrouter":
return {
openAiCompatTurnValidation: false,
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
};
case "kilocode":
return {
geminiThoughtSignatureSanitization: true,
geminiThoughtSignatureModelHints: ["gemini"],
};
default:
return undefined;
resolveProviderRuntimePlugin: vi.fn(({ provider }: { provider?: string }) => {
if (
!provider ||
![
"amazon-bedrock",
"anthropic",
"google",
"kilocode",
"kimi",
"kimi-code",
"mistral",
"openai",
"openai-codex",
"opencode",
"opencode-go",
"openrouter",
].includes(provider)
) {
return undefined;
}
return {
buildReplayPolicy: (context?: { modelId?: string; modelApi?: string }) => {
const modelId = context?.modelId?.toLowerCase() ?? "";
switch (provider) {
case "amazon-bedrock":
case "anthropic":
return {
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
preserveSignatures: true,
repairToolUseResultPairing: true,
validateAnthropicTurns: true,
allowSyntheticToolResults: true,
...(modelId.includes("claude") ? { dropThinkingBlocks: true } : {}),
};
case "google":
return {
sanitizeMode: "full",
sanitizeToolCallIds: true,
toolCallIdMode: "strict",
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
repairToolUseResultPairing: true,
applyAssistantFirstOrderingFix: true,
validateGeminiTurns: true,
validateAnthropicTurns: false,
allowSyntheticToolResults: true,
};
case "mistral":
return {
sanitizeToolCallIds: true,
toolCallIdMode: "strict9",
};
case "openai":
case "openai-codex":
return {
sanitizeMode: "images-only",
sanitizeToolCallIds: context?.modelApi === "openai-completions",
...(context?.modelApi === "openai-completions" ? { toolCallIdMode: "strict" } : {}),
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
};
case "kimi":
case "kimi-code":
return {
preserveSignatures: false,
};
case "openrouter":
case "opencode":
case "opencode-go":
return {
applyAssistantFirstOrderingFix: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
...(modelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
};
case "kilocode":
return modelId.includes("gemini")
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: undefined;
default:
return undefined;
}
},
};
}),
resolveProviderReplayPolicyWithPlugin: vi.fn(() => undefined),
resetProviderRuntimeHookCacheForTest: vi.fn(),
}));
@@ -79,6 +157,9 @@ describe("resolveTranscriptPolicy", () => {
});
expect(policy.sanitizeToolCallIds).toBe(false);
expect(policy.toolCallIdMode).toBeUndefined();
expect(policy.applyGoogleTurnOrdering).toBe(false);
expect(policy.validateGeminiTurns).toBe(false);
expect(policy.validateAnthropicTurns).toBe(false);
});
it("enables strict tool call id sanitization for openai-completions APIs", () => {
@@ -102,6 +183,20 @@ describe("resolveTranscriptPolicy", () => {
expect(policy.validateAnthropicTurns).toBe(true);
});
it("falls back to transport defaults when a plugin replay hook returns undefined", () => {
const policy = resolveTranscriptPolicy({
provider: "kilocode",
modelId: "kilocode-default",
modelApi: "openai-completions",
});
expect(policy.sanitizeToolCallIds).toBe(true);
expect(policy.toolCallIdMode).toBe("strict");
expect(policy.applyGoogleTurnOrdering).toBe(true);
expect(policy.validateGeminiTurns).toBe(true);
expect(policy.validateAnthropicTurns).toBe(true);
});
it("enables Anthropic-compatible policies for Bedrock provider", () => {
const policy = resolveTranscriptPolicy({
provider: "amazon-bedrock",

View File

@@ -1,17 +1,8 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveProviderReplayPolicyWithPlugin } from "../plugins/provider-runtime.js";
import type { ProviderRuntimeModel } from "../plugins/types.js";
import { resolveProviderRuntimePlugin } from "../plugins/provider-runtime.js";
import type { ProviderReplayPolicy, ProviderRuntimeModel } from "../plugins/types.js";
import { normalizeProviderId } from "./model-selection.js";
import { isGoogleModelApi } from "./pi-embedded-helpers/google.js";
import {
isAnthropicProviderFamily,
isOpenAiProviderFamily,
preservesAnthropicThinkingSignatures,
resolveTranscriptToolCallIdMode,
shouldDropThinkingBlocksForModel,
shouldSanitizeGeminiThoughtSignaturesForModel,
supportsOpenAiCompatTurnValidation,
} from "./provider-capabilities.js";
import type { ToolCallIdMode } from "./tool-call-id.js";
export type TranscriptSanitizeMode = "full" | "images-only";
@@ -34,30 +25,113 @@ export type TranscriptPolicy = {
allowSyntheticToolResults: boolean;
};
const OPENAI_MODEL_APIS = new Set([
"openai",
"openai-completions",
"openai-responses",
"openai-codex-responses",
]);
const DEFAULT_TRANSCRIPT_POLICY: TranscriptPolicy = {
sanitizeMode: "images-only",
sanitizeToolCallIds: false,
toolCallIdMode: undefined,
repairToolUseResultPairing: true,
preserveSignatures: false,
sanitizeThoughtSignatures: undefined,
sanitizeThinkingSignatures: false,
dropThinkingBlocks: false,
applyGoogleTurnOrdering: false,
validateGeminiTurns: false,
validateAnthropicTurns: false,
allowSyntheticToolResults: false,
};
function isOpenAiApi(modelApi?: string | null): boolean {
if (!modelApi) {
return false;
}
return OPENAI_MODEL_APIS.has(modelApi);
function isAnthropicApi(modelApi?: string | null): boolean {
return modelApi === "anthropic-messages" || modelApi === "bedrock-converse-stream";
}
function isOpenAiProvider(provider?: string | null): boolean {
return isOpenAiProviderFamily(provider);
function buildTransportReplayFallback(params: {
modelApi?: string | null;
modelId?: string | null;
}): ProviderReplayPolicy | undefined {
const isGoogle = isGoogleModelApi(params.modelApi);
const isAnthropic = isAnthropicApi(params.modelApi);
const isStrictOpenAiCompatible = params.modelApi === "openai-completions";
const requiresOpenAiCompatibleToolIdSanitization =
params.modelApi === "openai-completions" ||
params.modelApi === "openai-responses" ||
params.modelApi === "openai-codex-responses" ||
params.modelApi === "azure-openai-responses";
if (
!isGoogle &&
!isAnthropic &&
!isStrictOpenAiCompatible &&
!requiresOpenAiCompatibleToolIdSanitization
) {
return undefined;
}
const modelId = params.modelId?.toLowerCase() ?? "";
return {
...(isGoogle || isAnthropic ? { sanitizeMode: "full" as const } : {}),
...(isGoogle || isAnthropic || requiresOpenAiCompatibleToolIdSanitization
? {
sanitizeToolCallIds: true,
toolCallIdMode: "strict" as const,
}
: {}),
...(isAnthropic ? { preserveSignatures: true } : {}),
...(isGoogle
? {
sanitizeThoughtSignatures: {
allowBase64Only: true,
includeCamelCase: true,
},
}
: {}),
...(isAnthropic && modelId.includes("claude") ? { dropThinkingBlocks: true } : {}),
...(isGoogle || isStrictOpenAiCompatible ? { applyAssistantFirstOrderingFix: true } : {}),
...(isGoogle || isStrictOpenAiCompatible ? { validateGeminiTurns: true } : {}),
...(isAnthropic || isStrictOpenAiCompatible ? { validateAnthropicTurns: true } : {}),
...(isGoogle || isAnthropic ? { allowSyntheticToolResults: true } : {}),
};
}
function isAnthropicApi(modelApi?: string | null, provider?: string | null): boolean {
if (modelApi === "anthropic-messages" || modelApi === "bedrock-converse-stream") {
return true;
function mergeTranscriptPolicy(
policy: ProviderReplayPolicy | undefined,
basePolicy: TranscriptPolicy = DEFAULT_TRANSCRIPT_POLICY,
): TranscriptPolicy {
if (!policy) {
return basePolicy;
}
// MiniMax now uses openai-completions API, not anthropic-messages
return isAnthropicProviderFamily(provider);
return {
...basePolicy,
...(policy.sanitizeMode != null ? { sanitizeMode: policy.sanitizeMode } : {}),
...(typeof policy.sanitizeToolCallIds === "boolean"
? { sanitizeToolCallIds: policy.sanitizeToolCallIds }
: {}),
...(policy.toolCallIdMode ? { toolCallIdMode: policy.toolCallIdMode as ToolCallIdMode } : {}),
...(typeof policy.repairToolUseResultPairing === "boolean"
? { repairToolUseResultPairing: policy.repairToolUseResultPairing }
: {}),
...(typeof policy.preserveSignatures === "boolean"
? { preserveSignatures: policy.preserveSignatures }
: {}),
...(policy.sanitizeThoughtSignatures
? { sanitizeThoughtSignatures: policy.sanitizeThoughtSignatures }
: {}),
...(typeof policy.dropThinkingBlocks === "boolean"
? { dropThinkingBlocks: policy.dropThinkingBlocks }
: {}),
...(typeof policy.applyAssistantFirstOrderingFix === "boolean"
? { applyGoogleTurnOrdering: policy.applyAssistantFirstOrderingFix }
: {}),
...(typeof policy.validateGeminiTurns === "boolean"
? { validateGeminiTurns: policy.validateGeminiTurns }
: {}),
...(typeof policy.validateAnthropicTurns === "boolean"
? { validateAnthropicTurns: policy.validateAnthropicTurns }
: {}),
...(typeof policy.allowSyntheticToolResults === "boolean"
? { allowSyntheticToolResults: policy.allowSyntheticToolResults }
: {}),
};
}
export function resolveTranscriptPolicy(params: {
@@ -70,115 +144,33 @@ export function resolveTranscriptPolicy(params: {
model?: ProviderRuntimeModel;
}): TranscriptPolicy {
const provider = normalizeProviderId(params.provider ?? "");
const modelId = params.modelId ?? "";
const isGoogle = isGoogleModelApi(params.modelApi);
const isAnthropic = isAnthropicApi(params.modelApi, provider);
const isOpenAi = isOpenAiProvider(provider) || (!provider && isOpenAiApi(params.modelApi));
const isStrictOpenAiCompatible =
params.modelApi === "openai-completions" &&
!isOpenAi &&
supportsOpenAiCompatTurnValidation(provider);
const providerToolCallIdMode = resolveTranscriptToolCallIdMode(provider, modelId);
const isMistral = providerToolCallIdMode === "strict9";
const shouldSanitizeGeminiThoughtSignaturesForProvider =
shouldSanitizeGeminiThoughtSignaturesForModel({
provider,
modelId,
});
const requiresOpenAiCompatibleToolIdSanitization =
params.modelApi === "openai-completions" ||
(!isOpenAi &&
(params.modelApi === "openai-responses" ||
params.modelApi === "openai-codex-responses" ||
params.modelApi === "azure-openai-responses"));
// Anthropic Claude endpoints can reject replayed `thinking` blocks unless the
// original signatures are preserved byte-for-byte. Drop them at send-time to
// keep persisted sessions usable across follow-up turns.
const dropThinkingBlocks = shouldDropThinkingBlocksForModel({ provider, modelId });
const needsNonImageSanitize =
isGoogle || isAnthropic || isMistral || shouldSanitizeGeminiThoughtSignaturesForProvider;
const sanitizeToolCallIds =
isGoogle || isMistral || isAnthropic || requiresOpenAiCompatibleToolIdSanitization;
const toolCallIdMode: ToolCallIdMode | undefined = providerToolCallIdMode
? providerToolCallIdMode
: isMistral
? "strict9"
: sanitizeToolCallIds
? "strict"
: undefined;
// All providers need orphaned tool_result repair after history truncation.
// OpenAI rejects function_call_output items whose call_id has no matching
// function_call in the conversation, so the repair must run universally.
const repairToolUseResultPairing = true;
const sanitizeThoughtSignatures =
shouldSanitizeGeminiThoughtSignaturesForProvider || isGoogle
? { allowBase64Only: true, includeCamelCase: true }
: undefined;
const basePolicy: TranscriptPolicy = {
sanitizeMode: isOpenAi ? "images-only" : needsNonImageSanitize ? "full" : "images-only",
sanitizeToolCallIds:
(!isOpenAi && sanitizeToolCallIds) || requiresOpenAiCompatibleToolIdSanitization,
toolCallIdMode,
repairToolUseResultPairing,
preserveSignatures: isAnthropic && preservesAnthropicThinkingSignatures(provider),
sanitizeThoughtSignatures: isOpenAi ? undefined : sanitizeThoughtSignatures,
sanitizeThinkingSignatures: false,
dropThinkingBlocks,
applyGoogleTurnOrdering: !isOpenAi && (isGoogle || isStrictOpenAiCompatible),
validateGeminiTurns: !isOpenAi && (isGoogle || isStrictOpenAiCompatible),
validateAnthropicTurns: !isOpenAi && (isAnthropic || isStrictOpenAiCompatible),
allowSyntheticToolResults: !isOpenAi && (isGoogle || isAnthropic),
};
const pluginPolicy = provider
? resolveProviderReplayPolicyWithPlugin({
const runtimePlugin = provider
? resolveProviderRuntimePlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId,
modelApi: params.modelApi,
model: params.model,
},
})
: undefined;
if (!pluginPolicy) {
return basePolicy;
const context = {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId ?? "",
modelApi: params.modelApi,
model: params.model,
};
const pluginPolicy = runtimePlugin?.buildReplayPolicy?.(context);
if (pluginPolicy != null) {
return mergeTranscriptPolicy(pluginPolicy);
}
return {
...basePolicy,
...(pluginPolicy.sanitizeMode != null ? { sanitizeMode: pluginPolicy.sanitizeMode } : {}),
...(typeof pluginPolicy.sanitizeToolCallIds === "boolean"
? { sanitizeToolCallIds: pluginPolicy.sanitizeToolCallIds }
: {}),
...(pluginPolicy.toolCallIdMode ? { toolCallIdMode: pluginPolicy.toolCallIdMode } : {}),
...(typeof pluginPolicy.repairToolUseResultPairing === "boolean"
? { repairToolUseResultPairing: pluginPolicy.repairToolUseResultPairing }
: {}),
...(typeof pluginPolicy.preserveSignatures === "boolean"
? { preserveSignatures: pluginPolicy.preserveSignatures }
: {}),
...(pluginPolicy.sanitizeThoughtSignatures
? { sanitizeThoughtSignatures: pluginPolicy.sanitizeThoughtSignatures }
: {}),
...(typeof pluginPolicy.dropThinkingBlocks === "boolean"
? { dropThinkingBlocks: pluginPolicy.dropThinkingBlocks }
: {}),
...(typeof pluginPolicy.applyAssistantFirstOrderingFix === "boolean"
? { applyGoogleTurnOrdering: pluginPolicy.applyAssistantFirstOrderingFix }
: {}),
...(typeof pluginPolicy.allowSyntheticToolResults === "boolean"
? { allowSyntheticToolResults: pluginPolicy.allowSyntheticToolResults }
: {}),
};
return mergeTranscriptPolicy(
buildTransportReplayFallback({
modelApi: params.modelApi,
modelId: params.modelId,
}),
);
}

View File

@@ -63,9 +63,12 @@ export type {
ProviderReasoningOutputModeContext,
ProviderReplayPolicy,
ProviderReplayPolicyContext,
ProviderReplaySessionEntry,
ProviderReplaySessionState,
ProviderResolveDynamicModelContext,
ProviderResolvedUsageAuth,
ProviderSanitizeReplayHistoryContext,
ProviderToolSchemaDiagnostic,
ProviderResolveUsageAuthContext,
ProviderRuntimeModel,
ProviderThinkingPolicyContext,
@@ -108,21 +111,6 @@ export type {
export type { ChannelMessageActionContext } from "../channels/plugins/types.js";
export type { ChannelConfigUiHint, ChannelPlugin } from "../channels/plugins/types.plugin.js";
export type { PluginRuntime } from "../plugins/runtime/types.js";
export type {
BoundTaskFlowsRuntime,
BoundTaskRunsRuntime,
PluginRuntimeTaskFlows,
PluginRuntimeTaskRuns,
PluginRuntimeTasks,
} from "../plugins/runtime/runtime-tasks.js";
export type {
TaskFlowDetail,
TaskFlowView,
TaskRunAggregateSummary,
TaskRunCancelResult,
TaskRunDetail,
TaskRunView,
} from "../plugins/runtime/task-domain-types.js";
export { definePluginEntry } from "./plugin-entry.js";
export { buildPluginConfigSchema, emptyPluginConfigSchema } from "../plugins/config-schema.js";

View File

@@ -45,9 +45,12 @@ import type {
ProviderReasoningOutputModeContext,
ProviderReplayPolicy,
ProviderReplayPolicyContext,
ProviderReplaySessionEntry,
ProviderReplaySessionState,
ProviderResolvedUsageAuth,
ProviderResolveDynamicModelContext,
ProviderSanitizeReplayHistoryContext,
ProviderToolSchemaDiagnostic,
ProviderResolveUsageAuthContext,
ProviderRuntimeModel,
ProviderThinkingPolicyContext,
@@ -85,10 +88,13 @@ export type {
ProviderNormalizeModelIdContext,
ProviderReplayPolicy,
ProviderReplayPolicyContext,
ProviderReplaySessionEntry,
ProviderReplaySessionState,
ProviderPreparedRuntimeAuth,
ProviderReasoningOutputMode,
ProviderReasoningOutputModeContext,
ProviderResolvedUsageAuth,
ProviderToolSchemaDiagnostic,
ProviderPrepareExtraParamsContext,
ProviderPrepareDynamicModelContext,
ProviderPrepareRuntimeAuthContext,

View File

@@ -1,5 +1,6 @@
// Public stream-wrapper helpers for provider plugins.
export { createAnthropicToolPayloadCompatibilityWrapper } from "../agents/pi-embedded-runner/anthropic-family-tool-payload-compat.js";
export {
createBedrockNoCacheWrapper,
isAnthropicBedrockModel,
@@ -8,6 +9,7 @@ export {
createGoogleThinkingPayloadWrapper,
sanitizeGoogleThinkingPayload,
} from "../agents/pi-embedded-runner/google-stream-wrappers.js";
export { createMinimaxFastModeWrapper } from "../agents/pi-embedded-runner/minimax-stream-wrappers.js";
export {
createKilocodeWrapper,
createOpenRouterSystemCacheWrapper,
@@ -20,7 +22,16 @@ export {
} from "../agents/pi-embedded-runner/moonshot-thinking-stream-wrappers.js";
export {
createOpenAIAttributionHeadersWrapper,
createCodexNativeWebSearchWrapper,
createOpenAIDefaultTransportWrapper,
createOpenAIFastModeWrapper,
createOpenAIReasoningCompatibilityWrapper,
createOpenAIResponsesContextManagementWrapper,
createOpenAIServiceTierWrapper,
createOpenAITextVerbosityWrapper,
resolveOpenAIFastMode,
resolveOpenAIServiceTier,
resolveOpenAITextVerbosity,
} from "../agents/pi-embedded-runner/openai-stream-wrappers.js";
export { streamWithPayloadPatch } from "../agents/pi-embedded-runner/stream-payload-utils.js";
export {

View File

@@ -1,4 +1,8 @@
// Shared provider-tool helpers for plugin-owned schema compatibility rewrites.
export {
cleanSchemaForGemini,
GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS,
} from "../agents/schema/clean-for-gemini.js";
export const XAI_UNSUPPORTED_SCHEMA_KEYWORDS = new Set([
"minLength",
@@ -54,3 +58,45 @@ export function stripUnsupportedSchemaKeywords(
export function stripXaiUnsupportedKeywords(schema: unknown): unknown {
return stripUnsupportedSchemaKeywords(schema, XAI_UNSUPPORTED_SCHEMA_KEYWORDS);
}
export function findUnsupportedSchemaKeywords(
schema: unknown,
path: string,
unsupportedKeywords: ReadonlySet<string>,
): string[] {
if (!schema || typeof schema !== "object") {
return [];
}
if (Array.isArray(schema)) {
return schema.flatMap((item, index) =>
findUnsupportedSchemaKeywords(item, `${path}[${index}]`, unsupportedKeywords),
);
}
const record = schema as Record<string, unknown>;
const violations: string[] = [];
const properties =
record.properties && typeof record.properties === "object" && !Array.isArray(record.properties)
? (record.properties as Record<string, unknown>)
: undefined;
if (properties) {
for (const [key, value] of Object.entries(properties)) {
violations.push(
...findUnsupportedSchemaKeywords(value, `${path}.properties.${key}`, unsupportedKeywords),
);
}
}
for (const [key, value] of Object.entries(record)) {
if (key === "properties") {
continue;
}
if (unsupportedKeywords.has(key)) {
violations.push(`${path}.${key}`);
}
if (value && typeof value === "object") {
violations.push(
...findUnsupportedSchemaKeywords(value, `${path}.${key}`, unsupportedKeywords),
);
}
}
return violations;
}

View File

@@ -55,10 +55,10 @@ let resolveProviderSyntheticAuthWithPlugin: typeof import("./provider-runtime.js
let shouldDeferProviderSyntheticProfileAuthWithPlugin: typeof import("./provider-runtime.js").shouldDeferProviderSyntheticProfileAuthWithPlugin;
let sanitizeProviderReplayHistoryWithPlugin: typeof import("./provider-runtime.js").sanitizeProviderReplayHistoryWithPlugin;
let resolveProviderUsageSnapshotWithPlugin: typeof import("./provider-runtime.js").resolveProviderUsageSnapshotWithPlugin;
let resolveProviderCapabilitiesWithPlugin: typeof import("./provider-runtime.js").resolveProviderCapabilitiesWithPlugin;
let resolveProviderUsageAuthWithPlugin: typeof import("./provider-runtime.js").resolveProviderUsageAuthWithPlugin;
let resolveProviderXHighThinking: typeof import("./provider-runtime.js").resolveProviderXHighThinking;
let normalizeProviderToolSchemasWithPlugin: typeof import("./provider-runtime.js").normalizeProviderToolSchemasWithPlugin;
let inspectProviderToolSchemasWithPlugin: typeof import("./provider-runtime.js").inspectProviderToolSchemasWithPlugin;
let normalizeProviderResolvedModelWithPlugin: typeof import("./provider-runtime.js").normalizeProviderResolvedModelWithPlugin;
let prepareProviderDynamicModel: typeof import("./provider-runtime.js").prepareProviderDynamicModel;
let prepareProviderRuntimeAuth: typeof import("./provider-runtime.js").prepareProviderRuntimeAuth;
@@ -273,10 +273,10 @@ describe("provider-runtime", () => {
shouldDeferProviderSyntheticProfileAuthWithPlugin,
sanitizeProviderReplayHistoryWithPlugin,
resolveProviderUsageSnapshotWithPlugin,
resolveProviderCapabilitiesWithPlugin,
resolveProviderUsageAuthWithPlugin,
resolveProviderXHighThinking,
normalizeProviderToolSchemasWithPlugin,
inspectProviderToolSchemasWithPlugin,
normalizeProviderResolvedModelWithPlugin,
prepareProviderDynamicModel,
prepareProviderRuntimeAuth,
@@ -382,6 +382,29 @@ describe("provider-runtime", () => {
});
});
it("resolves stream wrapper hooks through hook-only aliases without provider ownership", () => {
const wrappedStreamFn = vi.fn();
resolvePluginProvidersMock.mockReturnValue([
{
id: "openai",
label: "OpenAI",
hookAliases: ["azure-openai-responses"],
auth: [],
wrapStreamFn: ({ streamFn }) => streamFn ?? wrappedStreamFn,
},
]);
expect(
wrapProviderStreamFn({
provider: "azure-openai-responses",
context: createDemoResolvedModelContext({
provider: "azure-openai-responses",
streamFn: wrappedStreamFn,
}),
}),
).toBe(wrappedStreamFn);
});
it("normalizes transport hooks without needing provider ownership", () => {
resolvePluginProvidersMock.mockReturnValue([
{
@@ -497,6 +520,7 @@ describe("provider-runtime", () => {
const normalizeToolSchemas = vi.fn(
({ tools }: Pick<ProviderNormalizeToolSchemasContext, "tools">): AnyAgentTool[] => tools,
);
const inspectToolSchemas = vi.fn(() => [] as { toolName: string; violations: string[] }[]);
const resolveReasoningOutputMode = vi.fn(() => "tagged" as const);
const resolveSyntheticAuth = vi.fn(() => ({
apiKey: "demo-local",
@@ -548,13 +572,11 @@ describe("provider-runtime", () => {
...providerConfig,
compat: { supportsUsageInStreaming: true },
}),
capabilities: {
providerFamily: "openai",
},
buildReplayPolicy,
sanitizeReplayHistory,
validateReplayTurns,
normalizeToolSchemas,
inspectToolSchemas,
resolveReasoningOutputMode,
prepareExtraParams: ({ extraParams }) => ({
...extraParams,
@@ -679,14 +701,6 @@ describe("provider-runtime", () => {
}),
});
expect(
resolveProviderCapabilitiesWithPlugin({
provider: DEMO_PROVIDER_ID,
}),
).toMatchObject({
providerFamily: "openai",
});
expect(
resolveProviderReplayPolicyWithPlugin({
provider: DEMO_PROVIDER_ID,
@@ -860,6 +874,16 @@ describe("provider-runtime", () => {
}),
).toEqual([DEMO_TOOL]);
expect(
inspectProviderToolSchemasWithPlugin({
provider: DEMO_PROVIDER_ID,
context: createDemoResolvedModelContext({
modelApi: MODEL.api,
tools: [DEMO_TOOL],
}),
}),
).toEqual([]);
expect(
normalizeProviderResolvedModelWithPlugin({
provider: DEMO_PROVIDER_ID,
@@ -1010,6 +1034,7 @@ describe("provider-runtime", () => {
sanitizeReplayHistory,
validateReplayTurns,
normalizeToolSchemas,
inspectToolSchemas,
resolveReasoningOutputMode,
refreshOAuth,
resolveSyntheticAuth,

View File

@@ -434,15 +434,6 @@ export function resolveProviderConfigApiKeyWithPlugin(params: {
return trimmed ? trimmed : undefined;
}
export function resolveProviderCapabilitiesWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}) {
return resolveProviderRuntimePlugin(params)?.capabilities;
}
export function resolveProviderReplayPolicyWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
@@ -483,6 +474,16 @@ export function normalizeProviderToolSchemasWithPlugin(params: {
return resolveProviderHookPlugin(params)?.normalizeToolSchemas?.(params.context) ?? undefined;
}
export function inspectProviderToolSchemasWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeToolSchemasContext;
}) {
return resolveProviderHookPlugin(params)?.inspectToolSchemas?.(params.context) ?? undefined;
}
export function resolveProviderReasoningOutputModeWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
@@ -521,7 +522,7 @@ export function wrapProviderStreamFn(params: {
env?: NodeJS.ProcessEnv;
context: ProviderWrapStreamFnContext;
}) {
return resolveProviderRuntimePlugin(params)?.wrapStreamFn?.(params.context) ?? undefined;
return resolveProviderHookPlugin(params)?.wrapStreamFn?.(params.context) ?? undefined;
}
export async function createProviderEmbeddingProvider(params: {

View File

@@ -11,7 +11,6 @@ import type {
AuthProfileStore,
} from "../agents/auth-profiles/types.js";
import type { ModelCatalogEntry } from "../agents/model-catalog.js";
import type { ProviderCapabilities } from "../agents/provider-capabilities.js";
import type { ProviderRequestTransportOverrides } from "../agents/provider-request-config.js";
import type { AnyAgentTool } from "../agents/tools/common.js";
import type { ThinkLevel } from "../auto-reply/thinking.js";
@@ -546,6 +545,16 @@ 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<string, unknown>;
/**
* Provider-owned replay/compaction transcript policy.
*
@@ -565,6 +574,8 @@ export type ProviderReplayPolicy = {
dropThinkingBlocks?: boolean;
repairToolUseResultPairing?: boolean;
applyAssistantFirstOrderingFix?: boolean;
validateGeminiTurns?: boolean;
validateAnthropicTurns?: boolean;
allowSyntheticToolResults?: boolean;
};
@@ -585,6 +596,16 @@ export type ProviderReplayPolicyContext = {
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.
*
@@ -595,6 +616,7 @@ export type ProviderSanitizeReplayHistoryContext = ProviderReplayPolicyContext &
sessionId: string;
messages: AgentMessage[];
allowedToolNames?: Iterable<string>;
sessionState?: ProviderReplaySessionState;
};
/**
@@ -606,6 +628,7 @@ export type ProviderSanitizeReplayHistoryContext = ProviderReplayPolicyContext &
export type ProviderValidateReplayTurnsContext = ProviderReplayPolicyContext & {
sessionId?: string;
messages: AgentMessage[];
sessionState?: ProviderReplaySessionState;
};
/**
@@ -618,6 +641,12 @@ export type ProviderNormalizeToolSchemasContext = ProviderReplayPolicyContext &
tools: AnyAgentTool[];
};
export type ProviderToolSchemaDiagnostic = {
toolName: string;
toolIndex?: number;
violations: string[];
};
/**
* Provider-owned reasoning output mode input.
*
@@ -1036,13 +1065,12 @@ export type ProviderPlugin = {
*/
resolveConfigApiKey?: (ctx: ProviderResolveConfigApiKeyContext) => string | null | undefined;
/**
* Static provider capability overrides consumed by shared transcript/tooling
* logic.
* @deprecated Legacy static capability bag kept only for compatibility.
*
* Use this when the provider behaves like OpenAI/Anthropic, needs transcript
* sanitization quirks, or requires provider-family hints.
* New provider behavior should use explicit hooks instead. Core replay and
* stream/runtime logic no longer consumes this field.
*/
capabilities?: Partial<ProviderCapabilities>;
capabilities?: ProviderCapabilities;
/**
* Provider-owned replay/compaction policy override.
*
@@ -1079,6 +1107,15 @@ export type ProviderPlugin = {
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.
*

View File

@@ -39,24 +39,6 @@ export function resolveReasoningOutputMode(params: {
return pluginMode;
}
const normalized = provider.toLowerCase();
// Check for exact matches or known prefixes/substrings for reasoning providers.
// Note: Ollama is intentionally excluded - its OpenAI-compatible endpoint
// handles reasoning natively via the `reasoning` field in streaming chunks,
// so tag-based enforcement is unnecessary and causes all output to be
// discarded as "(no output)" (#2279).
// Note: MiniMax is also intentionally excluded. In production it does not
// reliably wrap user-visible output in <final> tags, so forcing tag
// enforcement suppresses normal assistant replies.
if (
normalized === "google" ||
normalized === "google-gemini-cli" ||
normalized === "google-generative-ai"
) {
return "tagged";
}
return "native";
}

View File

@@ -1,8 +1,35 @@
import { describe, expect, it } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { parseBooleanValue } from "./boolean.js";
import { isReasoningTagProvider } from "./provider-utils.js";
import { splitShellArgs } from "./shell-argv.js";
const resolveProviderReasoningOutputModeWithPluginMock = vi.fn((params: { provider: string }) => {
switch (params.provider.toLowerCase()) {
case "google":
case "google-gemini-cli":
case "google-generative-ai":
case "minimax":
case "minimax-cn":
return "tagged" as const;
default:
return undefined;
}
});
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderReasoningOutputModeWithPlugin: (params: { provider: string }) =>
resolveProviderReasoningOutputModeWithPluginMock(params),
}));
let isReasoningTagProvider: typeof import("./provider-utils.js").isReasoningTagProvider;
beforeAll(async () => {
({ isReasoningTagProvider } = await import("./provider-utils.js"));
});
beforeEach(() => {
resolveProviderReasoningOutputModeWithPluginMock.mockClear();
});
describe("parseBooleanValue", () => {
it("handles boolean inputs", () => {
expect(parseBooleanValue(true)).toBe(true);
@@ -45,7 +72,7 @@ describe("parseBooleanValue", () => {
describe("isReasoningTagProvider", () => {
it.each([
{
name: "returns false for ollama - native reasoning field, no tags needed (#2279)",
name: "returns false for ollama when the provider plugin has no tagged override",
value: "ollama",
expected: false,
},
@@ -55,7 +82,7 @@ describe("isReasoningTagProvider", () => {
expected: false,
},
{
name: "returns true for google (gemini-api-key auth provider)",
name: "returns true for google via provider hook",
value: "google",
expected: true,
},
@@ -64,21 +91,21 @@ describe("isReasoningTagProvider", () => {
value: "Google",
expected: true,
},
{ name: "returns true for google-gemini-cli", value: "google-gemini-cli", expected: true },
{
name: "returns true for google-generative-ai",
value: "google-generative-ai",
name: "returns true for google-gemini-cli via provider hook",
value: "google-gemini-cli",
expected: true,
},
{
name: "returns false for minimax - does not reliably honor <final> wrappers in production",
value: "minimax",
expected: false,
name: "returns true for google-generative-ai via provider hook",
value: "google-generative-ai",
expected: true,
},
{ name: "returns true for minimax via provider hook", value: "minimax", expected: true },
{
name: "returns false for minimax-cn",
name: "returns true for minimax-cn via provider hook alias",
value: "minimax-cn",
expected: false,
expected: true,
},
{ name: "returns false for null", value: null, expected: false },
{ name: "returns false for undefined", value: undefined, expected: false },
@@ -91,7 +118,7 @@ describe("isReasoningTagProvider", () => {
value: string | null | undefined;
expected: boolean;
}>)("$name", ({ value, expected }) => {
expect(isReasoningTagProvider(value, { workspaceDir: process.cwd() })).toBe(expected);
expect(isReasoningTagProvider(value)).toBe(expected);
});
});