diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d2d5b337c..000f55bd114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -154,6 +154,7 @@ Docs: https://docs.openclaw.ai - Agents/tools: make `/tools` show the tools the current agent can actually use right now, add a compact default view with an optional detailed mode, and add a live “Available Right Now” section in the Control UI so it is easier to see what will work before you ask. - Microsoft Teams: migrate to the official Teams SDK and add AI-agent UX best practices including streaming 1:1 replies, welcome cards with prompt starters, feedback/reflection, informative status updates, typing indicators, and native AI labeling. (#51808) - Microsoft Teams: add message edit and delete support for sent messages, including in-thread fallbacks when no explicit target is provided. (#49925) +- Plugins/hooks: add async `requireApproval` to `before_tool_call` hooks, letting plugins pause tool execution and prompt the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the `/approve` command on any channel. The `/approve` command now handles both exec and plugin approvals with automatic fallback. - Skills/install metadata: add one-click install recipes to bundled skills (coding-agent, gh-issues, openai-whisper-api, session-logs, tmux, trello, weather) so the CLI and Control UI can offer dependency installation when requirements are missing. (#53411) Thanks @BunsDev. - Control UI/skills: add status-filter tabs (All / Ready / Needs Setup / Disabled) with counts, replace inline skill cards with a click-to-detail dialog showing requirements, toggle switch, install action, API key entry, source metadata, and homepage link. (#53411) Thanks @BunsDev. - Slack/interactive replies: restore rich reply parity for direct deliveries, auto-render simple trailing `Options:` lines as buttons/selects, improve Slack interactive setup defaults, and isolate reply controls from plugin interactive handlers. (#53389) Thanks @vincentkoc. diff --git a/apps/macos/Sources/OpenClawProtocol/GatewayModels.swift b/apps/macos/Sources/OpenClawProtocol/GatewayModels.swift index 924b284babd..758f90460ea 100644 --- a/apps/macos/Sources/OpenClawProtocol/GatewayModels.swift +++ b/apps/macos/Sources/OpenClawProtocol/GatewayModels.swift @@ -9,6 +9,7 @@ public enum ErrorCode: String, Codable, Sendable { case notPaired = "NOT_PAIRED" case agentTimeout = "AGENT_TIMEOUT" case invalidRequest = "INVALID_REQUEST" + case approvalNotFound = "APPROVAL_NOT_FOUND" case unavailable = "UNAVAILABLE" } @@ -3434,6 +3435,90 @@ public struct ExecApprovalResolveParams: Codable, Sendable { } } +public struct PluginApprovalRequestParams: Codable, Sendable { + public let pluginid: String? + public let title: String + public let description: String + public let severity: String? + public let toolname: String? + public let toolcallid: String? + public let agentid: String? + public let sessionkey: String? + public let turnsourcechannel: String? + public let turnsourceto: String? + public let turnsourceaccountid: String? + public let turnsourcethreadid: AnyCodable? + public let timeoutms: Int? + public let twophase: Bool? + + public init( + pluginid: String?, + title: String, + description: String, + severity: String?, + toolname: String?, + toolcallid: String?, + agentid: String?, + sessionkey: String?, + turnsourcechannel: String?, + turnsourceto: String?, + turnsourceaccountid: String?, + turnsourcethreadid: AnyCodable?, + timeoutms: Int?, + twophase: Bool?) + { + self.pluginid = pluginid + self.title = title + self.description = description + self.severity = severity + self.toolname = toolname + self.toolcallid = toolcallid + self.agentid = agentid + self.sessionkey = sessionkey + self.turnsourcechannel = turnsourcechannel + self.turnsourceto = turnsourceto + self.turnsourceaccountid = turnsourceaccountid + self.turnsourcethreadid = turnsourcethreadid + self.timeoutms = timeoutms + self.twophase = twophase + } + + private enum CodingKeys: String, CodingKey { + case pluginid = "pluginId" + case title + case description + case severity + case toolname = "toolName" + case toolcallid = "toolCallId" + case agentid = "agentId" + case sessionkey = "sessionKey" + case turnsourcechannel = "turnSourceChannel" + case turnsourceto = "turnSourceTo" + case turnsourceaccountid = "turnSourceAccountId" + case turnsourcethreadid = "turnSourceThreadId" + case timeoutms = "timeoutMs" + case twophase = "twoPhase" + } +} + +public struct PluginApprovalResolveParams: Codable, Sendable { + public let id: String + public let decision: String + + public init( + id: String, + decision: String) + { + self.id = id + self.decision = decision + } + + private enum CodingKeys: String, CodingKey { + case id + case decision + } +} + public struct DevicePairListParams: Codable, Sendable {} public struct DevicePairApproveParams: Codable, Sendable { diff --git a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift index 924b284babd..758f90460ea 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift @@ -9,6 +9,7 @@ public enum ErrorCode: String, Codable, Sendable { case notPaired = "NOT_PAIRED" case agentTimeout = "AGENT_TIMEOUT" case invalidRequest = "INVALID_REQUEST" + case approvalNotFound = "APPROVAL_NOT_FOUND" case unavailable = "UNAVAILABLE" } @@ -3434,6 +3435,90 @@ public struct ExecApprovalResolveParams: Codable, Sendable { } } +public struct PluginApprovalRequestParams: Codable, Sendable { + public let pluginid: String? + public let title: String + public let description: String + public let severity: String? + public let toolname: String? + public let toolcallid: String? + public let agentid: String? + public let sessionkey: String? + public let turnsourcechannel: String? + public let turnsourceto: String? + public let turnsourceaccountid: String? + public let turnsourcethreadid: AnyCodable? + public let timeoutms: Int? + public let twophase: Bool? + + public init( + pluginid: String?, + title: String, + description: String, + severity: String?, + toolname: String?, + toolcallid: String?, + agentid: String?, + sessionkey: String?, + turnsourcechannel: String?, + turnsourceto: String?, + turnsourceaccountid: String?, + turnsourcethreadid: AnyCodable?, + timeoutms: Int?, + twophase: Bool?) + { + self.pluginid = pluginid + self.title = title + self.description = description + self.severity = severity + self.toolname = toolname + self.toolcallid = toolcallid + self.agentid = agentid + self.sessionkey = sessionkey + self.turnsourcechannel = turnsourcechannel + self.turnsourceto = turnsourceto + self.turnsourceaccountid = turnsourceaccountid + self.turnsourcethreadid = turnsourcethreadid + self.timeoutms = timeoutms + self.twophase = twophase + } + + private enum CodingKeys: String, CodingKey { + case pluginid = "pluginId" + case title + case description + case severity + case toolname = "toolName" + case toolcallid = "toolCallId" + case agentid = "agentId" + case sessionkey = "sessionKey" + case turnsourcechannel = "turnSourceChannel" + case turnsourceto = "turnSourceTo" + case turnsourceaccountid = "turnSourceAccountId" + case turnsourcethreadid = "turnSourceThreadId" + case timeoutms = "timeoutMs" + case twophase = "twoPhase" + } +} + +public struct PluginApprovalResolveParams: Codable, Sendable { + public let id: String + public let decision: String + + public init( + id: String, + decision: String) + { + self.id = id + self.decision = decision + } + + private enum CodingKeys: String, CodingKey { + case id + case decision + } +} + public struct DevicePairListParams: Codable, Sendable {} public struct DevicePairApproveParams: Codable, Sendable { diff --git a/docs/.generated/config-baseline.json b/docs/.generated/config-baseline.json index 98879179848..d551b16708b 100644 --- a/docs/.generated/config-baseline.json +++ b/docs/.generated/config-baseline.json @@ -7124,7 +7124,7 @@ "advanced" ], "label": "Approvals", - "help": "Approval routing controls for forwarding exec approval requests to chat destinations outside the originating session. Keep this disabled unless operators need explicit out-of-band approval visibility.", + "help": "Approval routing controls for forwarding exec and plugin approval requests to chat destinations outside the originating session. Keep these disabled unless operators need explicit out-of-band approval visibility.", "hasChildren": true }, { @@ -7300,6 +7300,179 @@ "help": "Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider). Verify semantics per provider because destination format differs across channel integrations.", "hasChildren": false }, + { + "path": "approvals.plugin", + "kind": "core", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Forwarding", + "help": "Groups plugin-approval forwarding behavior including enablement, routing mode, filters, and explicit targets. Independent of exec approval forwarding. Configure here when plugin approval prompts must reach operational channels.", + "hasChildren": true + }, + { + "path": "approvals.plugin.agentFilter", + "kind": "core", + "type": "array", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Agent Filter", + "help": "Optional allowlist of agent IDs eligible for forwarded plugin approvals, for example `[\"primary\", \"ops-agent\"]`. Use this to limit forwarding blast radius.", + "hasChildren": true + }, + { + "path": "approvals.plugin.agentFilter.*", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "approvals.plugin.enabled", + "kind": "core", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Forward Plugin Approvals", + "help": "Enables forwarding of plugin approval requests to configured delivery destinations (default: false). Independent of approvals.exec.enabled.", + "hasChildren": false + }, + { + "path": "approvals.plugin.mode", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Forwarding Mode", + "help": "Controls where plugin approval prompts are sent: \"session\" uses origin chat, \"targets\" uses configured targets, and \"both\" sends to both paths.", + "hasChildren": false + }, + { + "path": "approvals.plugin.sessionFilter", + "kind": "core", + "type": "array", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "storage" + ], + "label": "Plugin Approval Session Filter", + "help": "Optional session-key filters matched as substring or regex-style patterns, for example `[\"discord:\", \"^agent:ops:\"]`. Use narrow patterns so only intended approval contexts are forwarded.", + "hasChildren": true + }, + { + "path": "approvals.plugin.sessionFilter.*", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "approvals.plugin.targets", + "kind": "core", + "type": "array", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Forwarding Targets", + "help": "Explicit delivery targets used when plugin approval forwarding mode includes targets, each with channel and destination details.", + "hasChildren": true + }, + { + "path": "approvals.plugin.targets.*", + "kind": "core", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": true + }, + { + "path": "approvals.plugin.targets.*.accountId", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Target Account ID", + "help": "Optional account selector for multi-account channel setups when plugin approvals must route through a specific account context.", + "hasChildren": false + }, + { + "path": "approvals.plugin.targets.*.channel", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Target Channel", + "help": "Channel/provider ID used for forwarded plugin approval delivery, such as discord, slack, or a plugin channel id.", + "hasChildren": false + }, + { + "path": "approvals.plugin.targets.*.threadId", + "kind": "core", + "type": [ + "number", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Target Thread ID", + "help": "Optional thread/topic target for channels that support threaded delivery of forwarded plugin approvals.", + "hasChildren": false + }, + { + "path": "approvals.plugin.targets.*.to", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Approval Target Destination", + "help": "Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider).", + "hasChildren": false + }, { "path": "audio", "kind": "core", @@ -49644,6 +49817,127 @@ "help": "Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.", "hasChildren": false }, + { + "path": "plugins.entries.litellm", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/litellm-provider", + "help": "OpenClaw LiteLLM provider plugin (plugin: litellm)", + "hasChildren": true + }, + { + "path": "plugins.entries.litellm.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/litellm-provider Config", + "help": "Plugin-defined config payload for litellm.", + "hasChildren": false + }, + { + "path": "plugins.entries.litellm.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/litellm-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.litellm.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.litellm.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.litellm.subagent", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Subagent Policy", + "help": "Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.", + "hasChildren": true + }, + { + "path": "plugins.entries.litellm.subagent.allowedModels", + "kind": "plugin", + "type": "array", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Plugin Subagent Allowed Models", + "help": "Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.", + "hasChildren": true + }, + { + "path": "plugins.entries.litellm.subagent.allowedModels.*", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "plugins.entries.litellm.subagent.allowModelOverride", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Plugin Subagent Model Override", + "help": "Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.", + "hasChildren": false + }, { "path": "plugins.entries.llm-task", "kind": "plugin", diff --git a/docs/.generated/config-baseline.jsonl b/docs/.generated/config-baseline.jsonl index 5219c53126f..5d7b0ef0126 100644 --- a/docs/.generated/config-baseline.jsonl +++ b/docs/.generated/config-baseline.jsonl @@ -1,4 +1,4 @@ -{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5532} +{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5554} {"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -638,7 +638,7 @@ {"recordType":"path","path":"agents.list.*.tools.sandbox.tools.deny","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"agents.list.*.tools.sandbox.tools.deny.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.workspace","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} -{"recordType":"path","path":"approvals","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approvals","help":"Approval routing controls for forwarding exec approval requests to chat destinations outside the originating session. Keep this disabled unless operators need explicit out-of-band approval visibility.","hasChildren":true} +{"recordType":"path","path":"approvals","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approvals","help":"Approval routing controls for forwarding exec and plugin approval requests to chat destinations outside the originating session. Keep these disabled unless operators need explicit out-of-band approval visibility.","hasChildren":true} {"recordType":"path","path":"approvals.exec","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Exec Approval Forwarding","help":"Groups exec-approval forwarding behavior including enablement, routing mode, filters, and explicit targets. Configure here when approval prompts must reach operational channels instead of only the origin thread.","hasChildren":true} {"recordType":"path","path":"approvals.exec.agentFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for forwarded approvals, for example `[\"primary\", \"ops-agent\"]`. Use this to limit forwarding blast radius and avoid notifying channels for unrelated agents.","hasChildren":true} {"recordType":"path","path":"approvals.exec.agentFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -652,6 +652,19 @@ {"recordType":"path","path":"approvals.exec.targets.*.channel","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Channel","help":"Channel/provider ID used for forwarded approval delivery, such as discord, slack, or a plugin channel id. Use valid channel IDs only so approvals do not silently fail due to unknown routes.","hasChildren":false} {"recordType":"path","path":"approvals.exec.targets.*.threadId","kind":"core","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Thread ID","help":"Optional thread/topic target for channels that support threaded delivery of forwarded approvals. Use this to keep approval traffic contained in operational threads instead of main channels.","hasChildren":false} {"recordType":"path","path":"approvals.exec.targets.*.to","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Destination","help":"Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider). Verify semantics per provider because destination format differs across channel integrations.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding","help":"Groups plugin-approval forwarding behavior including enablement, routing mode, filters, and explicit targets. Independent of exec approval forwarding. Configure here when plugin approval prompts must reach operational channels.","hasChildren":true} +{"recordType":"path","path":"approvals.plugin.agentFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for forwarded plugin approvals, for example `[\"primary\", \"ops-agent\"]`. Use this to limit forwarding blast radius.","hasChildren":true} +{"recordType":"path","path":"approvals.plugin.agentFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"approvals.plugin.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Forward Plugin Approvals","help":"Enables forwarding of plugin approval requests to configured delivery destinations (default: false). Independent of approvals.exec.enabled.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding Mode","help":"Controls where plugin approval prompts are sent: \"session\" uses origin chat, \"targets\" uses configured targets, and \"both\" sends to both paths.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin.sessionFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Plugin Approval Session Filter","help":"Optional session-key filters matched as substring or regex-style patterns, for example `[\"discord:\", \"^agent:ops:\"]`. Use narrow patterns so only intended approval contexts are forwarded.","hasChildren":true} +{"recordType":"path","path":"approvals.plugin.sessionFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"approvals.plugin.targets","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding Targets","help":"Explicit delivery targets used when plugin approval forwarding mode includes targets, each with channel and destination details.","hasChildren":true} +{"recordType":"path","path":"approvals.plugin.targets.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"approvals.plugin.targets.*.accountId","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Account ID","help":"Optional account selector for multi-account channel setups when plugin approvals must route through a specific account context.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin.targets.*.channel","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Channel","help":"Channel/provider ID used for forwarded plugin approval delivery, such as discord, slack, or a plugin channel id.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin.targets.*.threadId","kind":"core","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Thread ID","help":"Optional thread/topic target for channels that support threaded delivery of forwarded plugin approvals.","hasChildren":false} +{"recordType":"path","path":"approvals.plugin.targets.*.to","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Destination","help":"Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider).","hasChildren":false} {"recordType":"path","path":"audio","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Audio","help":"Global audio ingestion settings used before higher-level tools process speech or media content. Configure this when you need deterministic transcription behavior for voice notes and clips.","hasChildren":true} {"recordType":"path","path":"audio.transcription","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["media"],"label":"Audio Transcription","help":"Command-based transcription settings for converting audio files into text before agent handling. Keep a simple, deterministic command path here so failures are easy to diagnose in logs.","hasChildren":true} {"recordType":"path","path":"audio.transcription.command","kind":"core","type":"array","required":true,"deprecated":false,"sensitive":false,"tags":["media"],"label":"Audio Transcription Command","help":"Executable + args used to transcribe audio (first token must be a safe binary/path), for example `[\"whisper-cli\", \"--model\", \"small\", \"{input}\"]`. Prefer a pinned command so runtime environments behave consistently.","hasChildren":true} @@ -4313,6 +4326,15 @@ {"recordType":"path","path":"plugins.entries.line.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true} {"recordType":"path","path":"plugins.entries.line.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"plugins.entries.line.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.litellm","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/litellm-provider","help":"OpenClaw LiteLLM provider plugin (plugin: litellm)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.litellm.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/litellm-provider Config","help":"Plugin-defined config payload for litellm.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.litellm.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/litellm-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.litellm.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.litellm.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.litellm.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.litellm.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.litellm.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"plugins.entries.litellm.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false} {"recordType":"path","path":"plugins.entries.llm-task","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"LLM Task","help":"Generic JSON-only LLM tool for structured tasks callable from workflows. (plugin: llm-task)","hasChildren":true} {"recordType":"path","path":"plugins.entries.llm-task.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"LLM Task Config","help":"Plugin-defined config payload for llm-task.","hasChildren":true} {"recordType":"path","path":"plugins.entries.llm-task.config.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} diff --git a/docs/.generated/plugin-sdk-api-baseline.json b/docs/.generated/plugin-sdk-api-baseline.json index 8fc65896a73..22cab819bab 100644 --- a/docs/.generated/plugin-sdk-api-baseline.json +++ b/docs/.generated/plugin-sdk-api-baseline.json @@ -109,7 +109,7 @@ "exportName": "ChannelConfiguredBindingConversationRef", "kind": "type", "source": { - "line": 554, + "line": 569, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -118,7 +118,7 @@ "exportName": "ChannelConfiguredBindingMatch", "kind": "type", "source": { - "line": 559, + "line": 574, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -127,7 +127,7 @@ "exportName": "ChannelConfiguredBindingProvider", "kind": "type", "source": { - "line": 563, + "line": 578, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -136,7 +136,7 @@ "exportName": "ChannelGatewayContext", "kind": "type", "source": { - "line": 239, + "line": 243, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -190,7 +190,7 @@ "exportName": "ChannelSetupAdapter", "kind": "type", "source": { - "line": 56, + "line": 60, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1707,7 +1707,7 @@ "exportName": "ChannelAllowlistAdapter", "kind": "type", "source": { - "line": 498, + "line": 513, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1716,7 +1716,7 @@ "exportName": "ChannelAuthAdapter", "kind": "type", "source": { - "line": 363, + "line": 367, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1734,7 +1734,7 @@ "exportName": "ChannelCapabilitiesDiagnostics", "kind": "type", "source": { - "line": 47, + "line": 51, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1743,7 +1743,7 @@ "exportName": "ChannelCapabilitiesDisplayLine", "kind": "type", "source": { - "line": 42, + "line": 46, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1752,7 +1752,7 @@ "exportName": "ChannelCapabilitiesDisplayTone", "kind": "type", "source": { - "line": 40, + "line": 44, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1761,7 +1761,7 @@ "exportName": "ChannelCommandAdapter", "kind": "type", "source": { - "line": 445, + "line": 449, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1770,7 +1770,7 @@ "exportName": "ChannelConfigAdapter", "kind": "type", "source": { - "line": 91, + "line": 95, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1779,7 +1779,7 @@ "exportName": "ChannelConfiguredBindingConversationRef", "kind": "type", "source": { - "line": 554, + "line": 569, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1788,7 +1788,7 @@ "exportName": "ChannelConfiguredBindingMatch", "kind": "type", "source": { - "line": 559, + "line": 574, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1797,7 +1797,7 @@ "exportName": "ChannelConfiguredBindingProvider", "kind": "type", "source": { - "line": 563, + "line": 578, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1806,7 +1806,7 @@ "exportName": "ChannelDirectoryAdapter", "kind": "type", "source": { - "line": 407, + "line": 411, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1833,7 +1833,7 @@ "exportName": "ChannelElevatedAdapter", "kind": "type", "source": { - "line": 438, + "line": 442, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1842,7 +1842,7 @@ "exportName": "ChannelExecApprovalAdapter", "kind": "type", "source": { - "line": 464, + "line": 468, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1851,7 +1851,7 @@ "exportName": "ChannelExecApprovalForwardTarget", "kind": "type", "source": { - "line": 32, + "line": 36, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1860,7 +1860,7 @@ "exportName": "ChannelExecApprovalInitiatingSurfaceState", "kind": "type", "source": { - "line": 27, + "line": 31, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1869,7 +1869,7 @@ "exportName": "ChannelGatewayAdapter", "kind": "type", "source": { - "line": 347, + "line": 351, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1878,7 +1878,7 @@ "exportName": "ChannelGatewayContext", "kind": "type", "source": { - "line": 239, + "line": 243, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1887,7 +1887,7 @@ "exportName": "ChannelGroupAdapter", "kind": "type", "source": { - "line": 122, + "line": 126, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1905,7 +1905,7 @@ "exportName": "ChannelHeartbeatAdapter", "kind": "type", "source": { - "line": 373, + "line": 377, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1932,7 +1932,7 @@ "exportName": "ChannelLifecycleAdapter", "kind": "type", "source": { - "line": 450, + "line": 454, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1941,7 +1941,7 @@ "exportName": "ChannelLoginWithQrStartResult", "kind": "type", "source": { - "line": 318, + "line": 322, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1950,7 +1950,7 @@ "exportName": "ChannelLoginWithQrWaitResult", "kind": "type", "source": { - "line": 323, + "line": 327, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1959,7 +1959,7 @@ "exportName": "ChannelLogoutContext", "kind": "type", "source": { - "line": 328, + "line": 332, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -1968,7 +1968,7 @@ "exportName": "ChannelLogoutResult", "kind": "type", "source": { - "line": 312, + "line": 316, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2076,7 +2076,7 @@ "exportName": "ChannelOutboundAdapter", "kind": "type", "source": { - "line": 155, + "line": 159, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2085,7 +2085,7 @@ "exportName": "ChannelOutboundContext", "kind": "type", "source": { - "line": 128, + "line": 132, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2103,7 +2103,7 @@ "exportName": "ChannelPairingAdapter", "kind": "type", "source": { - "line": 336, + "line": 340, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2139,7 +2139,7 @@ "exportName": "ChannelResolveKind", "kind": "type", "source": { - "line": 418, + "line": 422, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2148,7 +2148,7 @@ "exportName": "ChannelResolverAdapter", "kind": "type", "source": { - "line": 428, + "line": 432, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2157,7 +2157,7 @@ "exportName": "ChannelResolveResult", "kind": "type", "source": { - "line": 420, + "line": 424, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2166,7 +2166,7 @@ "exportName": "ChannelSecurityAdapter", "kind": "type", "source": { - "line": 576, + "line": 591, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2193,7 +2193,7 @@ "exportName": "ChannelSetupAdapter", "kind": "type", "source": { - "line": 56, + "line": 60, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2211,7 +2211,7 @@ "exportName": "ChannelStatusAdapter", "kind": "type", "source": { - "line": 185, + "line": 189, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -2393,7 +2393,7 @@ "exportName": "formatDocsLink", "kind": "function", "source": { - "line": 5, + "line": 9, "path": "src/terminal/links.ts" } }, @@ -2429,7 +2429,7 @@ "exportName": "ChannelSetupAdapter", "kind": "type", "source": { - "line": 56, + "line": 60, "path": "src/channels/plugins/types.adapters.ts" } }, @@ -3180,7 +3180,7 @@ "exportName": "createChannelPluginBase", "kind": "function", "source": { - "line": 437, + "line": 447, "path": "src/plugin-sdk/core.ts" } }, @@ -3189,16 +3189,16 @@ "exportName": "createChatChannelPlugin", "kind": "function", "source": { - "line": 414, + "line": 424, "path": "src/plugin-sdk/core.ts" } }, { - "declaration": "export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions): DefinedPluginEntry;", + "declaration": "export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;", "exportName": "defineChannelPluginEntry", "kind": "function", "source": { - "line": 246, + "line": 251, "path": "src/plugin-sdk/core.ts" } }, @@ -3207,7 +3207,7 @@ "exportName": "definePluginEntry", "kind": "function", "source": { - "line": 91, + "line": 129, "path": "src/plugin-sdk/plugin-entry.ts" } }, @@ -3216,7 +3216,7 @@ "exportName": "defineSetupPluginEntry", "kind": "function", "source": { - "line": 277, + "line": 287, "path": "src/plugin-sdk/core.ts" } }, @@ -3531,7 +3531,7 @@ "exportName": "GatewayRequestHandlerOptions", "kind": "type", "source": { - "line": 112, + "line": 114, "path": "src/gateway/server-methods/types.ts" } }, @@ -4028,7 +4028,7 @@ "exportName": "definePluginEntry", "kind": "function", "source": { - "line": 91, + "line": 129, "path": "src/plugin-sdk/plugin-entry.ts" } }, @@ -4448,24 +4448,6 @@ "path": "src/plugins/provider-onboarding-config.ts" } }, - { - "declaration": "export function applyCloudflareAiGatewayConfig(cfg: OpenClawConfig, params?: { accountId?: string | undefined; gatewayId?: string | undefined; } | undefined): OpenClawConfig;", - "exportName": "applyCloudflareAiGatewayConfig", - "kind": "function", - "source": { - "line": 85, - "path": "extensions/cloudflare-ai-gateway/onboard.ts" - } - }, - { - "declaration": "export function applyCloudflareAiGatewayProviderConfig(cfg: OpenClawConfig, params?: { accountId?: string | undefined; gatewayId?: string | undefined; } | undefined): OpenClawConfig;", - "exportName": "applyCloudflareAiGatewayProviderConfig", - "kind": "function", - "source": { - "line": 41, - "path": "extensions/cloudflare-ai-gateway/onboard.ts" - } - }, { "declaration": "export function applyOnboardAuthAgentModelsAndProviders(cfg: OpenClawConfig, params: { agentModels: Record; providers: Record; }): OpenClawConfig;", "exportName": "applyOnboardAuthAgentModelsAndProviders", @@ -4529,24 +4511,6 @@ "path": "src/plugins/provider-onboarding-config.ts" } }, - { - "declaration": "export function applyVercelAiGatewayConfig(cfg: OpenClawConfig): OpenClawConfig;", - "exportName": "applyVercelAiGatewayConfig", - "kind": "function", - "source": { - "line": 27, - "path": "extensions/vercel-ai-gateway/onboard.ts" - } - }, - { - "declaration": "export function applyVercelAiGatewayProviderConfig(cfg: OpenClawConfig): OpenClawConfig;", - "exportName": "applyVercelAiGatewayProviderConfig", - "kind": "function", - "source": { - "line": 8, - "path": "extensions/vercel-ai-gateway/onboard.ts" - } - }, { "declaration": "export function createDefaultModelPresetAppliers(params: { resolveParams: (cfg: OpenClawConfig, ...args: TArgs) => Omit<{ providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | ... 4 more ... | \"ollama\"; ... 4 more ...; primaryModelRef?: string | undefined; }, \"primaryModelRef\"> | null | undefined; primaryModelRef: string; }): ProviderOnboardPresetAppliers<...>;", "exportName": "createDefaultModelPresetAppliers", @@ -4592,24 +4556,6 @@ "path": "src/plugins/provider-onboarding-config.ts" } }, - { - "declaration": "export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF: \"cloudflare-ai-gateway/claude-sonnet-4-5\";", - "exportName": "CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF", - "kind": "const", - "source": { - "line": 5, - "path": "src/agents/cloudflare-ai-gateway.ts" - } - }, - { - "declaration": "export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF: \"vercel-ai-gateway/anthropic/claude-opus-4.6\";", - "exportName": "VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF", - "kind": "const", - "source": { - "line": 6, - "path": "extensions/vercel-ai-gateway/onboard.ts" - } - }, { "declaration": "export type AgentModelAliasEntry = AgentModelAliasEntry;", "exportName": "AgentModelAliasEntry", @@ -5073,7 +5019,7 @@ "exportName": "ChannelGatewayContext", "kind": "type", "source": { - "line": 239, + "line": 243, "path": "src/channels/plugins/types.adapters.ts" } }, diff --git a/docs/.generated/plugin-sdk-api-baseline.jsonl b/docs/.generated/plugin-sdk-api-baseline.jsonl index 2ea77a60d0e..7687647b970 100644 --- a/docs/.generated/plugin-sdk-api-baseline.jsonl +++ b/docs/.generated/plugin-sdk-api-baseline.jsonl @@ -10,16 +10,16 @@ {"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"index","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":230,"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":68,"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":37,"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":554,"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":559,"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":563,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":239,"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":569,"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":574,"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":578,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":243,"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":13,"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":516,"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":482,"sourcePath":"src/channels/plugins/types.core.ts"} {"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\" | \"upload-file\";","entrypoint":"index","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"} {"declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"index","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/channels/plugins/types.plugin.ts"} -{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"index","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"index","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":60,"sourcePath":"src/channels/plugins/types.adapters.ts"} {"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"index","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.core.ts"} {"declaration":"export type ChannelSetupWizard = ChannelSetupWizard;","entrypoint":"index","exportName":"ChannelSetupWizard","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":247,"sourcePath":"src/channels/plugins/setup-wizard.ts"} {"declaration":"export type ChannelSetupWizardAllowFromEntry = ChannelSetupWizardAllowFromEntry;","entrypoint":"index","exportName":"ChannelSetupWizardAllowFromEntry","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":154,"sourcePath":"src/channels/plugins/setup-wizard.ts"} @@ -186,36 +186,36 @@ {"declaration":"export type ChannelAgentPromptAdapter = ChannelAgentPromptAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAgentPromptAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":463,"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":18,"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":23,"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":498,"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":363,"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":513,"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":367,"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":230,"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":47,"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":42,"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":40,"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":445,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelConfigAdapter = ChannelConfigAdapter;","entrypoint":"channel-runtime","exportName":"ChannelConfigAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":91,"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":554,"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":559,"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":563,"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":407,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelCapabilitiesDiagnostics = ChannelCapabilitiesDiagnostics;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDiagnostics","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":51,"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":46,"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":44,"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":449,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelConfigAdapter = ChannelConfigAdapter;","entrypoint":"channel-runtime","exportName":"ChannelConfigAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":95,"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":569,"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":574,"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":578,"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":411,"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":469,"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":467,"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":438,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":464,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelExecApprovalForwardTarget = ChannelExecApprovalForwardTarget;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalForwardTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelExecApprovalInitiatingSurfaceState = ChannelExecApprovalInitiatingSurfaceState;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalInitiatingSurfaceState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":27,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":347,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":239,"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":122,"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":442,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":468,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelExecApprovalForwardTarget = ChannelExecApprovalForwardTarget;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalForwardTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":36,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelExecApprovalInitiatingSurfaceState = ChannelExecApprovalInitiatingSurfaceState;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalInitiatingSurfaceState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":31,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":351,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":243,"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":126,"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":216,"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":373,"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":377,"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":116,"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":13,"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":450,"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":318,"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":323,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":328,"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":312,"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":454,"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":322,"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":327,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":332,"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":316,"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":209,"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":260,"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":516,"sourcePath":"src/channels/plugins/types.core.ts"} @@ -227,22 +227,22 @@ {"declaration":"export type ChannelMessageToolSchemaContribution = ChannelMessageToolSchemaContribution;","entrypoint":"channel-runtime","exportName":"ChannelMessageToolSchemaContribution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":51,"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":395,"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":122,"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":155,"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":128,"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":159,"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":132,"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":15,"sourcePath":"src/channels/plugins/types.core.ts"} -{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":336,"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":340,"sourcePath":"src/channels/plugins/types.adapters.ts"} {"declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"channel-runtime","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":76,"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":547,"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":538,"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":418,"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":428,"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":420,"sourcePath":"src/channels/plugins/types.adapters.ts"} -{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":576,"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":422,"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":432,"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":424,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":591,"sourcePath":"src/channels/plugins/types.adapters.ts"} {"declaration":"export type ChannelSecurityContext = ChannelSecurityContext;","entrypoint":"channel-runtime","exportName":"ChannelSecurityContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":254,"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":245,"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":56,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":60,"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":63,"sourcePath":"src/channels/plugins/types.core.ts"} -{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":185,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":189,"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":100,"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":279,"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":288,"sourcePath":"src/channels/plugins/types.core.ts"} @@ -262,11 +262,11 @@ {"declaration":"export function createOptionalChannelSetupSurface(params: OptionalChannelSetupParams): OptionalChannelSetupSurface;","entrypoint":"channel-setup","exportName":"createOptionalChannelSetupSurface","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":40,"sourcePath":"src/plugin-sdk/channel-setup.ts"} {"declaration":"export function createOptionalChannelSetupWizard(params: OptionalChannelSetupParams): ChannelSetupWizard;","entrypoint":"channel-setup","exportName":"createOptionalChannelSetupWizard","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":35,"sourcePath":"src/plugin-sdk/optional-channel-setup.ts"} {"declaration":"export function createTopLevelChannelDmPolicy(params: { label: string; channel: string; policyKey: string; allowFromKey: string; getCurrent: (cfg: OpenClawConfig) => DmPolicy; promptAllowFrom?: ((params: { cfg: OpenClawConfig; prompter: WizardPrompter; accountId?: string | undefined; }) => Promise<...>) | undefined; getAllowFrom?: ((cfg: OpenClawConfig) => (string | number)[] | undefined) | undefined; }): ChannelSetupDmPolicy;","entrypoint":"channel-setup","exportName":"createTopLevelChannelDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":411,"sourcePath":"src/channels/plugins/setup-wizard-helpers.ts"} -{"declaration":"export function formatDocsLink(path: string, label?: string | undefined, opts?: { fallback?: string | undefined; force?: boolean | undefined; } | undefined): string;","entrypoint":"channel-setup","exportName":"formatDocsLink","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":5,"sourcePath":"src/terminal/links.ts"} +{"declaration":"export function formatDocsLink(path: string, label?: string | undefined, opts?: { fallback?: string | undefined; force?: boolean | undefined; } | undefined): string;","entrypoint":"channel-setup","exportName":"formatDocsLink","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":9,"sourcePath":"src/terminal/links.ts"} {"declaration":"export function setSetupChannelEnabled(cfg: OpenClawConfig, channel: string, enabled: boolean): OpenClawConfig;","entrypoint":"channel-setup","exportName":"setSetupChannelEnabled","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":813,"sourcePath":"src/channels/plugins/setup-wizard-helpers.ts"} {"declaration":"export function splitSetupEntries(raw: string): string[];","entrypoint":"channel-setup","exportName":"splitSetupEntries","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"function","recordType":"export","sourceLine":80,"sourcePath":"src/channels/plugins/setup-wizard-helpers.ts"} {"declaration":"export const DEFAULT_ACCOUNT_ID: \"default\";","entrypoint":"channel-setup","exportName":"DEFAULT_ACCOUNT_ID","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"const","recordType":"export","sourceLine":3,"sourcePath":"src/routing/account-id.ts"} -{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-setup","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-setup","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":60,"sourcePath":"src/channels/plugins/types.adapters.ts"} {"declaration":"export type ChannelSetupDmPolicy = ChannelSetupDmPolicy;","entrypoint":"channel-setup","exportName":"ChannelSetupDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":93,"sourcePath":"src/channels/plugins/setup-wizard-types.ts"} {"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"channel-setup","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.core.ts"} {"declaration":"export type ChannelSetupWizard = ChannelSetupWizard;","entrypoint":"channel-setup","exportName":"ChannelSetupWizard","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":247,"sourcePath":"src/channels/plugins/setup-wizard.ts"} @@ -349,11 +349,11 @@ {"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;","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(params: { accounts?: Record | 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(params: CreateChannelPluginBaseOptions): CreatedChannelPluginBase;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":437,"sourcePath":"src/plugin-sdk/core.ts"} -{"declaration":"export function createChatChannelPlugin(params: { base: ChatChannelPluginBase; security?: ChannelSecurityAdapter | 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":414,"sourcePath":"src/plugin-sdk/core.ts"} -{"declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":246,"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":91,"sourcePath":"src/plugin-sdk/plugin-entry.ts"} -{"declaration":"export function defineSetupPluginEntry(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":277,"sourcePath":"src/plugin-sdk/core.ts"} +{"declaration":"export function createChannelPluginBase(params: CreateChannelPluginBaseOptions): CreatedChannelPluginBase;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":447,"sourcePath":"src/plugin-sdk/core.ts"} +{"declaration":"export function createChatChannelPlugin(params: { base: ChatChannelPluginBase; security?: ChannelSecurityAdapter | 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":424,"sourcePath":"src/plugin-sdk/core.ts"} +{"declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":251,"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":129,"sourcePath":"src/plugin-sdk/plugin-entry.ts"} +{"declaration":"export function defineSetupPluginEntry(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":287,"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"} @@ -388,7 +388,7 @@ {"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").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":138,"sourcePath":"src/plugin-sdk/core.ts"} {"declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":76,"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":112,"sourcePath":"src/gateway/server-methods/types.ts"} +{"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":114,"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":1105,"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":1496,"sourcePath":"src/plugins/types.ts"} @@ -443,7 +443,7 @@ {"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":91,"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":129,"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":1105,"sourcePath":"src/plugins/types.ts"} @@ -490,8 +490,6 @@ {"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1080,"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":76,"sourcePath":"src/plugins/provider-onboarding-config.ts"} -{"declaration":"export function applyCloudflareAiGatewayConfig(cfg: OpenClawConfig, params?: { accountId?: string | undefined; gatewayId?: string | undefined; } | undefined): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyCloudflareAiGatewayConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":85,"sourcePath":"extensions/cloudflare-ai-gateway/onboard.ts"} -{"declaration":"export function applyCloudflareAiGatewayProviderConfig(cfg: OpenClawConfig, params?: { accountId?: string | undefined; gatewayId?: string | undefined; } | undefined): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyCloudflareAiGatewayProviderConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":41,"sourcePath":"extensions/cloudflare-ai-gateway/onboard.ts"} {"declaration":"export function applyOnboardAuthAgentModelsAndProviders(cfg: OpenClawConfig, params: { agentModels: Record; providers: Record; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyOnboardAuthAgentModelsAndProviders","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":53,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function applyProviderConfigWithDefaultModel(cfg: OpenClawConfig, params: { agentModels: Record; providerId: string; api: \"github-copilot\" | \"openai-completions\" | ... 5 more ... | \"ollama\"; baseUrl: string; defaultModel: ModelDefinitionConfig; defaultModelId?: string | undefined; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyProviderConfigWithDefaultModel","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":131,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function applyProviderConfigWithDefaultModelPreset(cfg: OpenClawConfig, params: { providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | \"openai-codex-responses\" | \"anthropic-messages\" | \"google-generative-ai\" | \"bedrock-converse-stream\" | \"ollama\"; ... 4 more ...; primaryModelRef?: string | undefined; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyProviderConfigWithDefaultModelPreset","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":152,"sourcePath":"src/plugins/provider-onboarding-config.ts"} @@ -499,15 +497,11 @@ {"declaration":"export function applyProviderConfigWithDefaultModelsPreset(cfg: OpenClawConfig, params: { providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | \"openai-codex-responses\" | \"anthropic-messages\" | \"google-generative-ai\" | \"bedrock-converse-stream\" | \"ollama\"; ... 4 more ...; primaryModelRef?: string | undefined; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyProviderConfigWithDefaultModelsPreset","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":230,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function applyProviderConfigWithModelCatalog(cfg: OpenClawConfig, params: { agentModels: Record; providerId: string; api: \"github-copilot\" | \"openai-completions\" | ... 5 more ... | \"ollama\"; baseUrl: string; catalogModels: ModelDefinitionConfig[]; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyProviderConfigWithModelCatalog","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":272,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function applyProviderConfigWithModelCatalogPreset(cfg: OpenClawConfig, params: { providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | \"openai-codex-responses\" | \"anthropic-messages\" | \"google-generative-ai\" | \"bedrock-converse-stream\" | \"ollama\"; baseUrl: string; catalogModels: ModelDefinitionConfig[]; aliases?: readonly AgentModelAliasEntry[] | undefined; primaryModelRef?: string | undefined; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyProviderConfigWithModelCatalogPreset","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":304,"sourcePath":"src/plugins/provider-onboarding-config.ts"} -{"declaration":"export function applyVercelAiGatewayConfig(cfg: OpenClawConfig): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyVercelAiGatewayConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":27,"sourcePath":"extensions/vercel-ai-gateway/onboard.ts"} -{"declaration":"export function applyVercelAiGatewayProviderConfig(cfg: OpenClawConfig): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyVercelAiGatewayProviderConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":8,"sourcePath":"extensions/vercel-ai-gateway/onboard.ts"} {"declaration":"export function createDefaultModelPresetAppliers(params: { resolveParams: (cfg: OpenClawConfig, ...args: TArgs) => Omit<{ providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | ... 4 more ... | \"ollama\"; ... 4 more ...; primaryModelRef?: string | undefined; }, \"primaryModelRef\"> | null | undefined; primaryModelRef: string; }): ProviderOnboardPresetAppliers<...>;","entrypoint":"provider-onboard","exportName":"createDefaultModelPresetAppliers","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":213,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function createDefaultModelsPresetAppliers(params: { resolveParams: (cfg: OpenClawConfig, ...args: TArgs) => Omit<{ providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | ... 4 more ... | \"ollama\"; ... 4 more ...; primaryModelRef?: string | undefined; }, \"primaryModelRef\"> | null | undefined; primaryModelRef: string; }): ProviderOnboardPresetAppliers<...>;","entrypoint":"provider-onboard","exportName":"createDefaultModelsPresetAppliers","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":255,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function createModelCatalogPresetAppliers(params: { resolveParams: (cfg: OpenClawConfig, ...args: TArgs) => Omit<{ providerId: string; api: \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | ... 4 more ... | \"ollama\"; baseUrl: string; catalogModels: ModelDefinitionConfig[]; aliases?: readonly AgentModelAliasEntry[] | undefined; primaryModelRef?: string | undefined; }, \"primaryModelRef\"> | null | undefined; primaryModelRef: string; }): ProviderOnboardPresetAppliers<...>;","entrypoint":"provider-onboard","exportName":"createModelCatalogPresetAppliers","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":327,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export function ensureModelAllowlistEntry(params: { cfg: OpenClawConfig; modelRef: string; defaultProvider?: string | undefined; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"ensureModelAllowlistEntry","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":5,"sourcePath":"src/plugins/provider-model-allowlist.ts"} {"declaration":"export function withAgentModelAliases(existing: Record | undefined, aliases: readonly AgentModelAliasEntry[]): Record;","entrypoint":"provider-onboard","exportName":"withAgentModelAliases","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/plugins/provider-onboarding-config.ts"} -{"declaration":"export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF: \"cloudflare-ai-gateway/claude-sonnet-4-5\";","entrypoint":"provider-onboard","exportName":"CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"const","recordType":"export","sourceLine":5,"sourcePath":"src/agents/cloudflare-ai-gateway.ts"} -{"declaration":"export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF: \"vercel-ai-gateway/anthropic/claude-opus-4.6\";","entrypoint":"provider-onboard","exportName":"VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"const","recordType":"export","sourceLine":6,"sourcePath":"extensions/vercel-ai-gateway/onboard.ts"} {"declaration":"export type AgentModelAliasEntry = AgentModelAliasEntry;","entrypoint":"provider-onboard","exportName":"AgentModelAliasEntry","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"type","recordType":"export","sourceLine":21,"sourcePath":"src/plugins/provider-onboarding-config.ts"} {"declaration":"export type ModelApi = \"github-copilot\" | \"openai-completions\" | \"openai-responses\" | \"openai-codex-responses\" | \"anthropic-messages\" | \"google-generative-ai\" | \"bedrock-converse-stream\" | \"ollama\";","entrypoint":"provider-onboard","exportName":"ModelApi","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/config/types.models.ts"} {"declaration":"export type ModelDefinitionConfig = ModelDefinitionConfig;","entrypoint":"provider-onboard","exportName":"ModelDefinitionConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/config/types.models.ts"} @@ -558,7 +552,7 @@ {"declaration":"export function removeAckReactionAfterReply(params: { removeAfterReply: boolean; ackReactionPromise: Promise | null; ackReactionValue: string | null; remove: () => Promise; onError?: ((err: unknown) => void) | undefined; }): void;","entrypoint":"testing","exportName":"removeAckReactionAfterReply","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":81,"sourcePath":"src/channels/ack-reactions.ts"} {"declaration":"export function shouldAckReaction(params: AckReactionGateParams): boolean;","entrypoint":"testing","exportName":"shouldAckReaction","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/ack-reactions.ts"} {"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"testing","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"} -{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"} +{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":243,"sourcePath":"src/channels/plugins/types.adapters.ts"} {"declaration":"export type MockFn = MockFn;","entrypoint":"testing","exportName":"MockFn","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":5,"sourcePath":"src/test-utils/vitest-mock-fn.ts"} {"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"testing","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"} {"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"testing","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"} diff --git a/docs/automation/hooks.md b/docs/automation/hooks.md index 8dd74935865..15bbcd9e436 100644 --- a/docs/automation/hooks.md +++ b/docs/automation/hooks.md @@ -459,6 +459,44 @@ These hooks are not event-stream listeners; they let plugins synchronously adjus ### Plugin Hook Events +#### before_tool_call + +Runs before each tool call. Plugins can modify parameters, block the call, or request user approval. + +Return fields: + +- **`params`**: Override tool parameters (merged with original params) +- **`block`**: Set to `true` to block the tool call +- **`blockReason`**: Reason shown to the agent when blocked +- **`requireApproval`**: Pause execution and wait for user approval via channels + +The `requireApproval` field triggers native platform approval (Telegram buttons, Discord components, `/approve` command) instead of relying on the agent to cooperate: + +```typescript +{ + requireApproval: { + title: "Sensitive operation", + description: "This tool call modifies production data", + severity: "warning", // "info" | "warning" | "critical" + timeoutMs: 120000, // default: 120s + timeoutBehavior: "deny", // "allow" | "deny" (default) + onResolution: async (decision) => { + // Called after the user resolves: "allow-once", "allow-always", "deny", "timeout", or "cancelled" + }, + } +} +``` + +The `onResolution` callback is invoked with the final decision string after the approval resolves, times out, or is cancelled. It runs in-process within the plugin (not sent to the gateway). Use it to persist decisions, update caches, or perform cleanup. + +The `pluginId` field is stamped automatically by the hook runner from the plugin registration. When multiple plugins return `requireApproval`, the first one (highest priority) wins. + +`block` takes precedence over `requireApproval`: if the merged hook result has both `block: true` and a `requireApproval` field, the tool call is blocked immediately without triggering the approval flow. This ensures a higher-priority plugin's block cannot be overridden by a lower-priority plugin's approval request. + +If the gateway is unavailable or does not support plugin approvals, the tool call falls back to a soft block using the `description` as the block reason. + +#### Compaction lifecycle + Compaction lifecycle hooks exposed through the plugin hook runner: - **`before_compaction`**: Runs before compaction with count/token metadata diff --git a/docs/tools/exec-approvals.md b/docs/tools/exec-approvals.md index 194a5e10f92..ee475a131be 100644 --- a/docs/tools/exec-approvals.md +++ b/docs/tools/exec-approvals.md @@ -361,6 +361,35 @@ Reply in chat: /approve deny ``` +The `/approve` command handles both exec approvals and plugin approvals. If the ID does not match a pending exec approval, it automatically checks plugin approvals. + +### Plugin approval forwarding + +Plugin approval forwarding uses the same delivery pipeline as exec approvals but has its own +independent config under `approvals.plugin`. Enabling or disabling one does not affect the other. + +```json5 +{ + approvals: { + plugin: { + enabled: true, + mode: "targets", + agentFilter: ["main"], + targets: [ + { channel: "slack", to: "U12345678" }, + { channel: "telegram", to: "123456789" }, + ], + }, + }, +} +``` + +The config shape is identical to `approvals.exec`: `enabled`, `mode`, `agentFilter`, +`sessionFilter`, and `targets` work the same way. + +Channels that support interactive exec approval buttons (such as Telegram) also render buttons for +plugin approvals. Channels without adapter support fall back to plain text with `/approve` instructions. + ### Built-in chat approval clients Discord and Telegram can also act as explicit exec approval clients with channel-specific config. diff --git a/extensions/discord/src/channel.test.ts b/extensions/discord/src/channel.test.ts index 4313379593b..8a7a7daa75d 100644 --- a/extensions/discord/src/channel.test.ts +++ b/extensions/discord/src/channel.test.ts @@ -1,4 +1,8 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import type { + PluginApprovalRequest, + PluginApprovalResolved, +} from "../../../src/infra/plugin-approvals.js"; import type { PluginRuntime } from "../../../src/plugins/runtime/types.js"; import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; import type { ResolvedDiscordAccount } from "./accounts.js"; @@ -45,6 +49,38 @@ function createCfg(): OpenClawConfig { } as OpenClawConfig; } +function createPluginApprovalRequest( + overrides?: Partial, +): PluginApprovalRequest { + return { + id: "plugin:approval-1", + request: { + title: "Sensitive plugin action", + description: "The plugin asked to perform a sensitive action.", + severity: "warning", + pluginId: "plugin-test", + toolName: "plugin.tool", + agentId: "agent-1", + sessionKey: "agent:agent-1:discord:channel:123456789", + ...overrides, + }, + createdAtMs: 1_000, + expiresAtMs: 61_000, + }; +} + +function createPluginApprovalResolved( + request?: PluginApprovalRequest["request"], +): PluginApprovalResolved { + return { + id: "plugin:approval-1", + decision: "allow-once", + resolvedBy: "discord:123", + ts: 2_000, + request, + }; +} + function resolveAccount(cfg: OpenClawConfig): ResolvedDiscordAccount { return discordPlugin.config.resolveAccount(cfg, "default") as ResolvedDiscordAccount; } @@ -112,6 +148,115 @@ describe("discordPlugin outbound", () => { expect(result).toMatchObject({ channel: "discord", messageId: "m1" }); }); + it("builds interactive plugin approval pending payloads for Discord forwarding", () => { + const cfg = createCfg(); + cfg.channels!.discord!.execApprovals = { + enabled: true, + approvers: ["123"], + }; + const payload = discordPlugin.execApprovals?.buildPluginPendingPayload?.({ + cfg, + request: createPluginApprovalRequest(), + target: { channel: "discord", to: "user:123" }, + nowMs: 2_000, + }); + + expect(payload?.text).toContain("Plugin approval required"); + const discordData = (payload?.channelData as { discord?: { components?: unknown } } | undefined) + ?.discord; + expect(discordData?.components).toBeDefined(); + const componentsJson = JSON.stringify(discordData?.components ?? {}); + expect(componentsJson).toContain("Plugin Approval Required"); + expect(componentsJson).toContain("execapproval:id=plugin%3Aapproval-1;action=allow-once"); + const execApproval = (payload?.channelData as { execApproval?: { approvalId?: string } }) + ?.execApproval; + expect(execApproval?.approvalId).toBe("plugin:approval-1"); + }); + + it("neutralizes plugin approval mentions in forwarded text and components", () => { + const cfg = createCfg(); + cfg.channels!.discord!.execApprovals = { + enabled: true, + approvers: ["123"], + }; + const payload = discordPlugin.execApprovals?.buildPluginPendingPayload?.({ + cfg, + request: createPluginApprovalRequest({ + title: "Heads up @everyone <@123> <@&456>", + description: "route @here and <#789>", + }), + target: { channel: "discord", to: "user:123" }, + nowMs: 2_000, + }); + + const text = payload?.text ?? ""; + const componentsJson = JSON.stringify( + ((payload?.channelData as { discord?: { components?: unknown } } | undefined)?.discord + ?.components ?? {}) as object, + ); + + expect(text).toContain("@\u200beveryone"); + expect(text).toContain("@\u200bhere"); + expect(text).toContain("<@\u200b123>"); + expect(text).toContain("<@\u200b&456>"); + expect(text).toContain("<#\u200b789>"); + expect(text).not.toContain("@everyone"); + expect(text).not.toContain("@here"); + expect(componentsJson).not.toContain("@everyone"); + expect(componentsJson).not.toContain("@here"); + expect(componentsJson).not.toContain("<@123>"); + expect(componentsJson).not.toContain("<@&456>"); + expect(componentsJson).not.toContain("<#789>"); + }); + + it("falls back to non-interactive plugin approval pending payload when Discord exec approvals are disabled", () => { + const payload = discordPlugin.execApprovals?.buildPluginPendingPayload?.({ + cfg: createCfg(), + request: createPluginApprovalRequest(), + target: { channel: "discord", to: "user:123" }, + nowMs: 2_000, + }); + + expect(payload?.text).toContain("Plugin approval required"); + const channelData = payload?.channelData as + | { + execApproval?: { approvalId?: string; approvalSlug?: string }; + discord?: { components?: unknown }; + } + | undefined; + expect(channelData?.execApproval?.approvalId).toBe("plugin:approval-1"); + expect(channelData?.execApproval?.approvalSlug).toBe("plugin:a"); + expect(channelData?.discord?.components).toBeUndefined(); + }); + + it("builds rich plugin approval resolved payloads when request snapshot is available", () => { + const payload = discordPlugin.execApprovals?.buildPluginResolvedPayload?.({ + cfg: createCfg(), + resolved: createPluginApprovalResolved(createPluginApprovalRequest().request), + target: { channel: "discord", to: "user:123" }, + }); + + expect(payload?.text).toContain("Plugin approval allowed once"); + const discordData = (payload?.channelData as { discord?: { components?: unknown } } | undefined) + ?.discord; + expect(discordData?.components).toBeDefined(); + const componentsJson = JSON.stringify(discordData?.components ?? {}); + expect(componentsJson).toContain("Plugin Approval: Allowed (once)"); + }); + + it("falls back to plain text plugin resolved payload when request snapshot is missing", () => { + const payload = discordPlugin.execApprovals?.buildPluginResolvedPayload?.({ + cfg: createCfg(), + resolved: createPluginApprovalResolved(undefined), + target: { channel: "discord", to: "user:123" }, + }); + + expect(payload?.text).toContain("Plugin approval allowed once"); + const discordData = (payload?.channelData as { discord?: { components?: unknown } } | undefined) + ?.discord; + expect(discordData?.components).toBeUndefined(); + }); + it("uses direct Discord probe helpers for status probes", async () => { const runtimeProbeDiscord = vi.fn(async () => { throw new Error("runtime Discord probe should not be used"); diff --git a/extensions/discord/src/channel.ts b/extensions/discord/src/channel.ts index 8d748e61bc3..ccb27f63b16 100644 --- a/extensions/discord/src/channel.ts +++ b/extensions/discord/src/channel.ts @@ -14,8 +14,12 @@ import { createRuntimeDirectoryLiveAdapter, } from "openclaw/plugin-sdk/directory-runtime"; import { + buildPluginApprovalRequestMessage, + buildPluginApprovalResolvedMessage, createRuntimeOutboundDelegates, resolveOutboundSendDep, + type PluginApprovalRequest, + type PluginApprovalResolved, } from "openclaw/plugin-sdk/infra-runtime"; import { normalizeMessageChannel } from "openclaw/plugin-sdk/routing"; import { @@ -28,6 +32,7 @@ import { type ResolvedDiscordAccount, } from "./accounts.js"; import { auditDiscordChannelPermissions, collectDiscordAuditChannelIds } from "./audit.js"; +import type { DiscordComponentMessageSpec } from "./components.js"; import { listDiscordDirectoryGroupsFromConfig, listDiscordDirectoryPeersFromConfig, @@ -88,6 +93,7 @@ async function loadDiscordProbeRuntime() { const meta = getChatChannelMeta("discord"); const REQUIRED_DISCORD_PERMISSIONS = ["ViewChannel", "SendMessages"] as const; +const DISCORD_EXEC_APPROVAL_KEY = "execapproval"; const resolveDiscordDmPolicy = createScopedDmSecurityResolver({ channelKey: "discord", @@ -116,6 +122,147 @@ function formatDiscordIntents(intents?: { ].join(" "); } +function encodeCustomIdValue(value: string): string { + return encodeURIComponent(value); +} + +function buildDiscordExecApprovalCustomId( + approvalId: string, + action: "allow-once" | "allow-always" | "deny", +): string { + return [ + `${DISCORD_EXEC_APPROVAL_KEY}:id=${encodeCustomIdValue(approvalId)}`, + `action=${action}`, + ].join(";"); +} + +function formatDiscordApprovalPreview(value: string, maxChars: number): string { + const trimmed = value + .replace(/@everyone/gi, "@\u200beveryone") + .replace(/@here/gi, "@\u200bhere") + .replace(/<@/g, "<@\u200b") + .replace(/<#/g, "<#\u200b") + .trim(); + const raw = trimmed.length > maxChars ? `${trimmed.slice(0, maxChars)}...` : trimmed; + return raw.replace(/`/g, "\u200b`"); +} + +function buildDiscordPluginPendingComponentSpec(params: { + request: PluginApprovalRequest; +}): DiscordComponentMessageSpec { + const request = params.request.request; + const severity = request.severity ?? "warning"; + const severityLabel = + severity === "critical" ? "Critical" : severity === "info" ? "Info" : "Warning"; + const accentColor = + severity === "critical" ? "#ED4245" : severity === "info" ? "#5865F2" : "#FAA61A"; + const expiresAtSeconds = Math.max(0, Math.floor(params.request.expiresAtMs / 1000)); + const metadataLines: string[] = [`- Severity: ${severityLabel}`]; + if (request.toolName) { + metadataLines.push(`- Tool: ${request.toolName}`); + } + if (request.pluginId) { + metadataLines.push(`- Plugin: ${request.pluginId}`); + } + if (request.agentId) { + metadataLines.push(`- Agent: ${request.agentId}`); + } + return { + container: { accentColor }, + blocks: [ + { type: "text", text: "## Plugin Approval Required" }, + { type: "text", text: "A plugin action needs your approval." }, + { type: "separator", divider: true, spacing: "small" }, + { + type: "text", + text: `### Title\n\`\`\`\n${formatDiscordApprovalPreview(request.title, 500)}\n\`\`\``, + }, + { + type: "text", + text: `### Description\n${formatDiscordApprovalPreview(request.description, 1000)}`, + }, + { type: "text", text: metadataLines.join("\n") }, + { + type: "actions", + buttons: [ + { + label: "Allow once", + style: "success", + internalCustomId: buildDiscordExecApprovalCustomId(params.request.id, "allow-once"), + }, + { + label: "Always allow", + style: "primary", + internalCustomId: buildDiscordExecApprovalCustomId(params.request.id, "allow-always"), + }, + { + label: "Deny", + style: "danger", + internalCustomId: buildDiscordExecApprovalCustomId(params.request.id, "deny"), + }, + ], + }, + { type: "separator", divider: false, spacing: "small" }, + { type: "text", text: `-# Expires · ID: ${params.request.id}` }, + ], + }; +} + +function buildDiscordPluginResolvedComponentSpec(params: { + resolved: PluginApprovalResolved; +}): DiscordComponentMessageSpec | undefined { + const request = params.resolved.request; + if (!request) { + return undefined; + } + const decisionLabel = + params.resolved.decision === "allow-once" + ? "Allowed (once)" + : params.resolved.decision === "allow-always" + ? "Allowed (always)" + : "Denied"; + const accentColor = + params.resolved.decision === "deny" + ? "#ED4245" + : params.resolved.decision === "allow-always" + ? "#5865F2" + : "#57F287"; + const metadataLines: string[] = []; + if (request.toolName) { + metadataLines.push(`- Tool: ${request.toolName}`); + } + if (request.pluginId) { + metadataLines.push(`- Plugin: ${request.pluginId}`); + } + if (request.agentId) { + metadataLines.push(`- Agent: ${request.agentId}`); + } + return { + container: { accentColor }, + blocks: [ + { type: "text", text: `## Plugin Approval: ${decisionLabel}` }, + { + type: "text", + text: params.resolved.resolvedBy ? `Resolved by ${params.resolved.resolvedBy}` : "Resolved", + }, + { type: "separator", divider: true, spacing: "small" }, + { + type: "text", + text: `### Title\n\`\`\`\n${formatDiscordApprovalPreview(request.title, 500)}\n\`\`\``, + }, + { + type: "text", + text: `### Description\n${formatDiscordApprovalPreview(request.description, 1000)}`, + }, + ...(metadataLines.length > 0 + ? [{ type: "text" as const, text: metadataLines.join("\n") }] + : []), + { type: "separator", divider: false, spacing: "small" }, + { type: "text", text: `-# ID: ${params.resolved.id}` }, + ], + }; +} + const discordMessageActions: ChannelMessageActionAdapter = { describeMessageTool: (ctx) => getDiscordRuntime().channel.discord.messageActions?.describeMessageTool?.(ctx) ?? null, @@ -293,6 +440,55 @@ export const discordPlugin: ChannelPlugin shouldSuppressForwardingFallback: ({ cfg, target }) => (normalizeMessageChannel(target.channel) ?? target.channel) === "discord" && isDiscordExecApprovalClientEnabled({ cfg, accountId: target.accountId }), + buildPluginPendingPayload: ({ cfg, request, target, nowMs }) => { + const text = formatDiscordApprovalPreview( + buildPluginApprovalRequestMessage(request, nowMs), + 10_000, + ); + const execApproval = { + approvalId: request.id, + approvalSlug: request.id.slice(0, 8), + allowedDecisions: ["allow-once", "allow-always", "deny"] as const, + }; + const normalizedChannel = normalizeMessageChannel(target.channel) ?? target.channel; + const interactiveEnabled = + normalizedChannel === "discord" && + isDiscordExecApprovalClientEnabled({ cfg, accountId: target.accountId }); + if (!interactiveEnabled) { + return { + text, + channelData: { + execApproval, + }, + }; + } + return { + text, + channelData: { + execApproval, + discord: { + components: buildDiscordPluginPendingComponentSpec({ request }), + }, + }, + }; + }, + buildPluginResolvedPayload: ({ resolved }) => { + const componentSpec = buildDiscordPluginResolvedComponentSpec({ resolved }); + const text = formatDiscordApprovalPreview( + buildPluginApprovalResolvedMessage(resolved), + 10_000, + ); + return componentSpec + ? { + text, + channelData: { + discord: { + components: componentSpec, + }, + }, + } + : { text }; + }, }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => listDiscordDirectoryPeersFromConfig(params), diff --git a/extensions/discord/src/components.ts b/extensions/discord/src/components.ts index 09978217a3b..c20f0f89d2f 100644 --- a/extensions/discord/src/components.ts +++ b/extensions/discord/src/components.ts @@ -51,6 +51,8 @@ export type DiscordComponentButtonSpec = { style?: DiscordComponentButtonStyle; url?: string; callbackData?: string; + /** Internal use only: bypass dynamic component ids with a fixed custom id. */ + internalCustomId?: string; emoji?: { name: string; id?: string; @@ -719,10 +721,16 @@ function createButtonComponent(params: { return { component: new DynamicLinkButton() }; } const componentId = params.componentId ?? createShortId("btn_"); - const customId = buildDiscordComponentCustomId({ - componentId, - modalId: params.modalId, - }); + const internalCustomId = + typeof params.spec.internalCustomId === "string" && params.spec.internalCustomId.trim() + ? params.spec.internalCustomId.trim() + : undefined; + const customId = + internalCustomId ?? + buildDiscordComponentCustomId({ + componentId, + modalId: params.modalId, + }); class DynamicButton extends Button { label = params.spec.label; customId = customId; @@ -730,6 +738,11 @@ function createButtonComponent(params: { emoji = params.spec.emoji; disabled = params.spec.disabled ?? false; } + if (internalCustomId) { + return { + component: new DynamicButton(), + }; + } return { component: new DynamicButton(), entry: { diff --git a/extensions/discord/src/monitor/exec-approvals.test.ts b/extensions/discord/src/monitor/exec-approvals.test.ts index 2c3e7cc07fd..5c8cb233139 100644 --- a/extensions/discord/src/monitor/exec-approvals.test.ts +++ b/extensions/discord/src/monitor/exec-approvals.test.ts @@ -46,7 +46,9 @@ const mockRestPatch = vi.hoisted(() => vi.fn()); const mockRestDelete = vi.hoisted(() => vi.fn()); const gatewayClientStarts = vi.hoisted(() => vi.fn()); const gatewayClientStops = vi.hoisted(() => vi.fn()); -const gatewayClientRequests = vi.hoisted(() => vi.fn(async () => ({ ok: true }))); +const gatewayClientRequests = vi.hoisted(() => + vi.fn(async (_method?: string, _params?: unknown) => ({ ok: true })), +); const gatewayClientParams = vi.hoisted(() => [] as Array>); const mockGatewayClientCtor = vi.hoisted(() => vi.fn()); const mockResolveGatewayConnectionAuth = vi.hoisted(() => vi.fn()); @@ -85,8 +87,8 @@ vi.mock("openclaw/plugin-sdk/gateway-runtime", async (importOriginal) => { stop() { gatewayClientStops(); } - async request() { - return gatewayClientRequests(); + async request(method: string, params?: unknown) { + return gatewayClientRequests(method, params); } } return { @@ -237,6 +239,7 @@ type DiscordExecApprovalHandlerInstance = InstanceType< >; type ExecApprovalRequest = import("./exec-approvals.js").ExecApprovalRequest; +type PluginApprovalRequest = import("./exec-approvals.js").PluginApprovalRequest; type ExecApprovalButtonContext = import("./exec-approvals.js").ExecApprovalButtonContext; function createTestingDeps() { @@ -372,8 +375,8 @@ type ExecApprovalHandlerInternals = { string, { discordMessageId: string; discordChannelId: string; timeoutId: NodeJS.Timeout } >; - requestCache: Map; - handleApprovalRequested: (request: ExecApprovalRequest) => Promise; + requestCache: Map; + handleApprovalRequested: (request: ExecApprovalRequest | PluginApprovalRequest) => Promise; handleApprovalTimeout: (approvalId: string, source?: "channel" | "dm") => Promise; }; @@ -409,6 +412,39 @@ function createRequest( }; } +function createPluginRequest( + overrides: Partial = {}, +): PluginApprovalRequest { + return { + id: "plugin:test-id", + request: { + title: "Sensitive plugin action", + description: "The plugin wants to run a sensitive tool action.", + severity: "warning", + toolName: "plugin.tool", + pluginId: "plugin-test", + agentId: "test-agent", + sessionKey: "agent:test-agent:discord:channel:999888777", + ...overrides, + }, + createdAtMs: Date.now(), + expiresAtMs: Date.now() + 60000, + }; +} + +function createMockButtonInteraction(userId: string) { + const reply = vi.fn().mockResolvedValue(undefined); + const acknowledge = vi.fn().mockResolvedValue(undefined); + const followUp = vi.fn().mockResolvedValue(undefined); + const interaction = { + userId, + reply, + acknowledge, + followUp, + } as unknown as ButtonInteraction; + return { interaction, reply, acknowledge, followUp }; +} + beforeEach(() => { mockRestPost.mockReset(); mockRestPatch.mockReset(); @@ -690,6 +726,104 @@ describe("DiscordExecApprovalHandler.shouldHandle", () => { }); }); +describe("DiscordExecApprovalHandler plugin approvals", () => { + beforeEach(() => { + mockRestPost.mockClear().mockResolvedValue({ id: "mock-message", channel_id: "mock-channel" }); + mockRestPatch.mockClear().mockResolvedValue({}); + mockRestDelete.mockClear().mockResolvedValue({}); + }); + + it("delivers plugin approval requests with interactive approval buttons", async () => { + const handler = createHandler({ enabled: true, approvers: ["123"] }); + const internals = getHandlerInternals(handler); + mockSuccessfulDmDelivery({ throwOnUnexpectedRoute: true }); + + await internals.handleApprovalRequested(createPluginRequest()); + + const dmCall = mockRestPost.mock.calls.find( + (call) => call[0] === Routes.channelMessages("dm-1"), + ) as [string, { body?: unknown }] | undefined; + expect(dmCall).toBeDefined(); + expect(dmCall?.[1]?.body).toBeDefined(); + const bodyJson = JSON.stringify(dmCall?.[1]?.body ?? {}); + expect(bodyJson).toContain("Plugin Approval Required"); + expect(bodyJson).toContain("plugin:test-id"); + expect(bodyJson).toContain("execapproval:id=plugin%3Atest-id;action=allow-once"); + + clearPendingTimeouts(handler); + }); + + it("handles plugin approvals end-to-end via gateway event, button resolve, and card update", async () => { + const handler = createHandler({ enabled: true, approvers: ["123"] }); + mockSuccessfulDmDelivery({ + noteChannelId: "999888777", + expectedNoteText: "I sent the allowed approvers DMs", + throwOnUnexpectedRoute: true, + }); + + await handler.start(); + try { + const onEvent = gatewayClientParams[0]?.onEvent as + | ((evt: { event: string; payload: unknown }) => void) + | undefined; + expect(typeof onEvent).toBe("function"); + + const request = createPluginRequest(); + onEvent?.({ + event: "plugin.approval.requested", + payload: request, + }); + + await vi.waitFor(() => { + expect(mockRestPost).toHaveBeenCalledWith( + Routes.channelMessages("dm-1"), + expect.objectContaining({ + body: expect.objectContaining({ + components: expect.any(Array), + }), + }), + ); + }); + + const button = new ExecApprovalButton({ handler }); + const { interaction, acknowledge } = createMockButtonInteraction("123"); + await button.run(interaction, { id: request.id, action: "allow-once" }); + + expect(acknowledge).toHaveBeenCalledTimes(1); + expect(gatewayClientRequests).toHaveBeenCalledWith("plugin.approval.resolve", { + id: request.id, + decision: "allow-once", + }); + + onEvent?.({ + event: "plugin.approval.resolved", + payload: { + id: request.id, + decision: "allow-once", + resolvedBy: "discord:123", + ts: Date.now(), + request: request.request, + }, + }); + + await vi.waitFor(() => { + expect(mockRestPatch).toHaveBeenCalledWith( + Routes.channelMessage("dm-1", "msg-1"), + expect.objectContaining({ body: expect.any(Object) }), + ); + }); + const patchCall = mockRestPatch.mock.calls.find( + (call) => call[0] === Routes.channelMessage("dm-1", "msg-1"), + ) as [string, { body?: unknown }] | undefined; + const patchBody = JSON.stringify(patchCall?.[1]?.body ?? {}); + expect(patchBody).toContain("Plugin Approval: Allowed (once)"); + } finally { + clearPendingTimeouts(handler); + await handler.stop(); + } + }); +}); + // ─── DiscordExecApprovalHandler.getApprovers ────────────────────────────────── describe("DiscordExecApprovalHandler.getApprovers", () => { @@ -719,6 +853,40 @@ describe("DiscordExecApprovalHandler.getApprovers", () => { }); }); +describe("DiscordExecApprovalHandler.resolveApproval", () => { + it("routes non-prefixed approval IDs to exec.approval.resolve", async () => { + const handler = createHandler({ enabled: true, approvers: ["123"] }); + await handler.start(); + + try { + const ok = await handler.resolveApproval("exec-123", "allow-once"); + expect(ok).toBe(true); + expect(gatewayClientRequests).toHaveBeenCalledWith("exec.approval.resolve", { + id: "exec-123", + decision: "allow-once", + }); + } finally { + await handler.stop(); + } + }); + + it("routes plugin-prefixed approval IDs to plugin.approval.resolve", async () => { + const handler = createHandler({ enabled: true, approvers: ["123"] }); + await handler.start(); + + try { + const ok = await handler.resolveApproval("plugin:abc-123", "deny"); + expect(ok).toBe(true); + expect(gatewayClientRequests).toHaveBeenCalledWith("plugin.approval.resolve", { + id: "plugin:abc-123", + decision: "deny", + }); + } finally { + await handler.stop(); + } + }); +}); + // ─── ExecApprovalButton authorization ───────────────────────────────────────── describe("ExecApprovalButton", () => { @@ -756,7 +924,7 @@ describe("ExecApprovalButton", () => { await button.run(interaction, data); expect(reply).toHaveBeenCalledWith({ - content: "⛔ You are not authorized to approve exec requests.", + content: "⛔ You are not authorized to approve requests.", ephemeral: true, }); expect(acknowledge).not.toHaveBeenCalled(); @@ -992,8 +1160,8 @@ describe("DiscordExecApprovalHandler timeout cleanup", () => { const requestA = { ...createRequest(), id: "abc" }; const requestB = { ...createRequest(), id: "abc2" }; - internals.requestCache.set("abc", requestA); - internals.requestCache.set("abc2", requestB); + internals.requestCache.set("abc", { kind: "exec", request: requestA }); + internals.requestCache.set("abc2", { kind: "exec", request: requestB }); const timeoutIdA = setTimeout(() => {}, 0); const timeoutIdB = setTimeout(() => {}, 0); diff --git a/extensions/discord/src/monitor/exec-approvals.ts b/extensions/discord/src/monitor/exec-approvals.ts index 48420c982fd..de78b56492c 100644 --- a/extensions/discord/src/monitor/exec-approvals.ts +++ b/extensions/discord/src/monitor/exec-approvals.ts @@ -21,6 +21,8 @@ import type { ExecApprovalDecision, ExecApprovalRequest, ExecApprovalResolved, + PluginApprovalRequest, + PluginApprovalResolved, } from "openclaw/plugin-sdk/infra-runtime"; import { normalizeAccountId, @@ -34,7 +36,12 @@ import * as sendShared from "../send.shared.js"; import { DiscordUiContainer } from "../ui.js"; const EXEC_APPROVAL_KEY = "execapproval"; -export type { ExecApprovalRequest, ExecApprovalResolved }; +export type { + ExecApprovalRequest, + ExecApprovalResolved, + PluginApprovalRequest, + PluginApprovalResolved, +}; /** Extract Discord channel ID from a session key like "agent:main:discord:channel:123456789" */ export function extractDiscordChannelId(sessionKey?: string | null): string | null { @@ -58,6 +65,12 @@ type PendingApproval = { timeoutId: NodeJS.Timeout; }; +type ApprovalKind = "exec" | "plugin"; + +type CachedApprovalRequest = + | { kind: "exec"; request: ExecApprovalRequest } + | { kind: "plugin"; request: PluginApprovalRequest }; + function encodeCustomIdValue(value: string): string { return encodeURIComponent(value); } @@ -102,6 +115,16 @@ export function parseExecApprovalData( }; } +function resolveApprovalKindFromId(approvalId: string): ApprovalKind { + return approvalId.startsWith("plugin:") ? "plugin" : "exec"; +} + +function isPluginApprovalRequest( + request: ExecApprovalRequest | PluginApprovalRequest, +): request is PluginApprovalRequest { + return resolveApprovalKindFromId(request.id) === "plugin"; +} + type ExecApprovalContainerParams = { cfg: OpenClawConfig; accountId: string; @@ -192,11 +215,11 @@ class ExecApprovalActionRow extends Row