fix: repair main CI contracts and release checks

This commit is contained in:
Josh Lehman
2026-03-17 13:57:11 -07:00
parent 5a2a4abc12
commit fa3376d64d
9 changed files with 370 additions and 82 deletions

View File

@@ -23200,6 +23200,56 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.accounts.*.dmChannelRetry",
"kind": "channel",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "channels.mattermost.accounts.*.dmChannelRetry.initialDelayMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.accounts.*.dmChannelRetry.maxDelayMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.accounts.*.dmChannelRetry.maxRetries",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.accounts.*.dmChannelRetry.timeoutMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.accounts.*.dmPolicy",
"kind": "channel",
@@ -23709,6 +23759,56 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.dmChannelRetry",
"kind": "channel",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "channels.mattermost.dmChannelRetry.initialDelayMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.dmChannelRetry.maxDelayMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.dmChannelRetry.maxRetries",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.dmChannelRetry.timeoutMs",
"kind": "channel",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.mattermost.dmPolicy",
"kind": "channel",
@@ -39699,7 +39799,7 @@
"network"
],
"label": "Control UI Allowed Origins",
"help": "Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com). Required for non-loopback Control UI deployments unless dangerous Host-header fallback is explicitly enabled.",
"help": "Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com). Required for non-loopback Control UI deployments unless dangerous Host-header fallback is explicitly enabled. Setting [\"*\"] means allow any browser origin and should be avoided outside tightly controlled local testing.",
"hasChildren": true
},
{
@@ -41038,7 +41138,7 @@
"access"
],
"label": "Hooks Allowed Agent IDs",
"help": "Allowlist of agent IDs that hook mappings are allowed to target when selecting execution agents. Use this to constrain automation events to dedicated service agents.",
"help": "Allowlist of agent IDs that hook mappings are allowed to target when selecting execution agents. Use this to constrain automation events to dedicated service agents and reduce blast radius if a hook token is exposed.",
"hasChildren": true
},
{
@@ -42156,7 +42256,7 @@
"security"
],
"label": "Hooks Auth Token",
"help": "Shared bearer token checked by hooks ingress for request authentication before mappings run. Use environment substitution and rotate regularly when webhook endpoints are internet-accessible.",
"help": "Shared bearer token checked by hooks ingress for request authentication before mappings run. Treat holders as full-trust callers for the hook ingress surface, not as a separate non-owner role. Use environment substitution and rotate regularly when webhook endpoints are internet-accessible.",
"hasChildren": false
},
{
@@ -46269,6 +46369,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.chutes",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "@openclaw/chutes-provider",
"help": "OpenClaw Chutes.ai provider plugin (plugin: chutes)",
"hasChildren": true
},
{
"path": "plugins.entries.chutes.config",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "@openclaw/chutes-provider Config",
"help": "Plugin-defined config payload for chutes.",
"hasChildren": false
},
{
"path": "plugins.entries.chutes.enabled",
"kind": "plugin",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Enable @openclaw/chutes-provider",
"hasChildren": false
},
{
"path": "plugins.entries.chutes.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.chutes.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.chutes.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.chutes.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.chutes.subagent.allowedModels.*",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.chutes.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.cloudflare-ai-gateway",
"kind": "plugin",

View File

@@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5457}
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5476}
{"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}
@@ -2086,6 +2086,11 @@
{"recordType":"path","path":"channels.mattermost.accounts.*.commands.nativeSkills","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.configWrites","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dangerouslyAllowNameMatching","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmChannelRetry","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmChannelRetry.initialDelayMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmChannelRetry.maxDelayMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmChannelRetry.maxRetries","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmChannelRetry.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.dmPolicy","kind":"channel","type":"string","required":true,"enumValues":["pairing","allowlist","open","disabled"],"defaultValue":"pairing","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -2130,6 +2135,11 @@
{"recordType":"path","path":"channels.mattermost.configWrites","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Config Writes","help":"Allow Mattermost to write config in response to channel events/commands (default: true).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dangerouslyAllowNameMatching","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.defaultAccount","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry.initialDelayMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry.maxDelayMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry.maxRetries","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmPolicy","kind":"channel","type":"string","required":true,"enumValues":["pairing","allowlist","open","disabled"],"defaultValue":"pairing","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -3563,7 +3573,7 @@
{"recordType":"path","path":"gateway.channelMaxRestartsPerHour","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance"],"label":"Gateway Channel Max Restarts Per Hour","help":"Maximum number of health-monitor-initiated channel restarts allowed within a rolling one-hour window. Once hit, further restarts are skipped until the window expires. Default: 10.","hasChildren":false}
{"recordType":"path","path":"gateway.channelStaleEventThresholdMinutes","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Gateway Channel Stale Event Threshold (min)","help":"How many minutes a connected channel can go without receiving any event before the health monitor treats it as a stale socket and triggers a restart. Default: 30.","hasChildren":false}
{"recordType":"path","path":"gateway.controlUi","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Control UI","help":"Control UI hosting settings including enablement, pathing, and browser-origin/auth hardening behavior. Keep UI exposure minimal and pair with strong auth controls before internet-facing deployments.","hasChildren":true}
{"recordType":"path","path":"gateway.controlUi.allowedOrigins","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","network"],"label":"Control UI Allowed Origins","help":"Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com). Required for non-loopback Control UI deployments unless dangerous Host-header fallback is explicitly enabled.","hasChildren":true}
{"recordType":"path","path":"gateway.controlUi.allowedOrigins","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","network"],"label":"Control UI Allowed Origins","help":"Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com). Required for non-loopback Control UI deployments unless dangerous Host-header fallback is explicitly enabled. Setting [\"*\"] means allow any browser origin and should be avoided outside tightly controlled local testing.","hasChildren":true}
{"recordType":"path","path":"gateway.controlUi.allowedOrigins.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"gateway.controlUi.allowInsecureAuth","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access","advanced","network","security"],"label":"Insecure Control UI Auth Toggle","help":"Loosens strict browser auth checks for Control UI when you must run a non-standard setup. Keep this off unless you trust your network and proxy path, because impersonation risk is higher.","hasChildren":false}
{"recordType":"path","path":"gateway.controlUi.basePath","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["network","storage"],"label":"Control UI Base Path","help":"Optional URL prefix where the Control UI is served (e.g. /openclaw).","hasChildren":false}
@@ -3667,7 +3677,7 @@
{"recordType":"path","path":"gateway.trustedProxies","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Gateway Trusted Proxy CIDRs","help":"CIDR/IP allowlist of upstream proxies permitted to provide forwarded client identity headers. Keep this list narrow so untrusted hops cannot impersonate users.","hasChildren":true}
{"recordType":"path","path":"gateway.trustedProxies.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"hooks","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Hooks","help":"Inbound webhook automation surface for mapping external events into wake or agent actions in OpenClaw. Keep this locked down with explicit token/session/agent controls before exposing it beyond trusted networks.","hasChildren":true}
{"recordType":"path","path":"hooks.allowedAgentIds","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Hooks Allowed Agent IDs","help":"Allowlist of agent IDs that hook mappings are allowed to target when selecting execution agents. Use this to constrain automation events to dedicated service agents.","hasChildren":true}
{"recordType":"path","path":"hooks.allowedAgentIds","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Hooks Allowed Agent IDs","help":"Allowlist of agent IDs that hook mappings are allowed to target when selecting execution agents. Use this to constrain automation events to dedicated service agents and reduce blast radius if a hook token is exposed.","hasChildren":true}
{"recordType":"path","path":"hooks.allowedAgentIds.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"hooks.allowedSessionKeyPrefixes","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","storage"],"label":"Hooks Allowed Session Key Prefixes","help":"Allowlist of accepted session-key prefixes for inbound hook requests when caller-provided keys are enabled. Use narrow prefixes to prevent arbitrary session-key injection.","hasChildren":true}
{"recordType":"path","path":"hooks.allowedSessionKeyPrefixes.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3754,7 +3764,7 @@
{"recordType":"path","path":"hooks.path","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Hooks Endpoint Path","help":"HTTP path used by the hooks endpoint (for example `/hooks`) on the gateway control server. Use a non-guessable path and combine it with token validation for defense in depth.","hasChildren":false}
{"recordType":"path","path":"hooks.presets","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Hooks Presets","help":"Named hook preset bundles applied at load time to seed standard mappings and behavior defaults. Keep preset usage explicit so operators can audit which automations are active.","hasChildren":true}
{"recordType":"path","path":"hooks.presets.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"hooks.token","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Hooks Auth Token","help":"Shared bearer token checked by hooks ingress for request authentication before mappings run. Use environment substitution and rotate regularly when webhook endpoints are internet-accessible.","hasChildren":false}
{"recordType":"path","path":"hooks.token","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Hooks Auth Token","help":"Shared bearer token checked by hooks ingress for request authentication before mappings run. Treat holders as full-trust callers for the hook ingress surface, not as a separate non-owner role. Use environment substitution and rotate regularly when webhook endpoints are internet-accessible.","hasChildren":false}
{"recordType":"path","path":"hooks.transformsDir","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Hooks Transforms Directory","help":"Base directory for hook transform modules referenced by mapping transform.module paths. Use a controlled repo directory so dynamic imports remain reviewable and predictable.","hasChildren":false}
{"recordType":"path","path":"logging","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Logging","help":"Logging behavior controls for severity, output destinations, formatting, and sensitive-data redaction. Keep levels and redaction strict enough for production while preserving useful diagnostics.","hasChildren":true}
{"recordType":"path","path":"logging.consoleLevel","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["observability"],"label":"Console Log Level","help":"Console-specific log threshold: \"silent\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\", or \"trace\" for terminal output control. Use this to keep local console quieter while retaining richer file logging if needed.","hasChildren":false}
@@ -4093,6 +4103,15 @@
{"recordType":"path","path":"plugins.entries.byteplus.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.byteplus.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.byteplus.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.chutes","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/chutes-provider","help":"OpenClaw Chutes.ai provider plugin (plugin: chutes)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.chutes.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/chutes-provider Config","help":"Plugin-defined config payload for chutes.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.chutes.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/chutes-provider","hasChildren":false}
{"recordType":"path","path":"plugins.entries.chutes.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.chutes.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.chutes.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.chutes.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.chutes.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.chutes.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.cloudflare-ai-gateway","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/cloudflare-ai-gateway-provider","help":"OpenClaw Cloudflare AI Gateway provider plugin (plugin: cloudflare-ai-gateway)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/cloudflare-ai-gateway-provider Config","help":"Plugin-defined config payload for cloudflare-ai-gateway.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/cloudflare-ai-gateway-provider","hasChildren":false}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/chutes-provider",
"version": "2026.3.17",
"version": "2026.3.14",
"private": true,
"description": "OpenClaw Chutes.ai provider plugin",
"type": "module",

View File

@@ -45,7 +45,6 @@ export {
buildMentionedCardContent,
type MentionTarget,
} from "./src/mention.js";
export { feishuPlugin } from "./src/channel.js";
export default defineChannelPluginEntry({
id: "feishu",

View File

@@ -1,2 +1,3 @@
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";
export * from "./src/runtime.js";

View File

@@ -1,42 +1,10 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ResolvedSlackAccount } from "../../../../extensions/slack/src/accounts.js";
import type { SlackMessageEvent } from "../../../../extensions/slack/src/types.js";
import type { MsgContext } from "../../../auto-reply/templating.js";
import type { OpenClawConfig } from "../../../config/config.js";
import { inboundCtxCapture } from "./inbound-testkit.js";
import { expectChannelInboundContextContract } from "./suites.js";
const dispatchInboundMessageMock = vi.hoisted(() =>
vi.fn(
async (params: {
ctx: MsgContext;
replyOptions?: { onReplyStart?: () => void | Promise<void> };
}) => {
await Promise.resolve(params.replyOptions?.onReplyStart?.());
return { queuedFinal: false, counts: { tool: 0, block: 0, final: 0 } };
},
),
);
vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/reply-runtime")>();
return {
...actual,
dispatchInboundMessage: vi.fn(async (params: { ctx: MsgContext }) => {
inboundCtxCapture.ctx = params.ctx;
return await dispatchInboundMessageMock(params);
}),
dispatchInboundMessageWithDispatcher: vi.fn(async (params: { ctx: MsgContext }) => {
inboundCtxCapture.ctx = params.ctx;
return await dispatchInboundMessageMock(params);
}),
dispatchInboundMessageWithBufferedDispatcher: vi.fn(async (params: { ctx: MsgContext }) => {
inboundCtxCapture.ctx = params.ctx;
return await dispatchInboundMessageMock(params);
}),
};
});
vi.mock("../../../../extensions/signal/src/send.js", () => ({
sendMessageSignal: vi.fn(),
sendTypingSignal: vi.fn(async () => true),
@@ -62,10 +30,6 @@ vi.mock("../../../../extensions/whatsapp/src/auto-reply/deliver-reply.js", () =>
deliverWebReply: vi.fn(async () => {}),
}));
const { processDiscordMessage } =
await import("../../../../extensions/discord/src/monitor/message-handler.process.js");
const { createBaseDiscordMessageContext, createDiscordDirectMessageContextOverrides } =
await import("../../../../extensions/discord/src/monitor/message-handler.test-harness.js");
const { finalizeInboundContext } = await import("../../../auto-reply/reply/inbound-context.js");
const { prepareSlackMessage } =
await import("../../../../extensions/slack/src/monitor/message-handler/prepare.js");
@@ -102,20 +66,33 @@ function createSlackMessage(overrides: Partial<SlackMessageEvent>): SlackMessage
describe("channel inbound contract", () => {
beforeEach(() => {
inboundCtxCapture.ctx = undefined;
dispatchInboundMessageMock.mockClear();
});
it("keeps Discord inbound context finalized", async () => {
const messageCtx = await createBaseDiscordMessageContext({
cfg: { messages: {} },
ackReactionScope: "direct",
...createDiscordDirectMessageContextOverrides(),
const ctx = finalizeInboundContext({
Body: "hi",
BodyForAgent: "hi",
RawBody: "hi",
CommandBody: "hi",
BodyForCommands: "hi",
From: "discord:U1",
To: "channel:c1",
SessionKey: "agent:main:discord:direct:u1",
AccountId: "default",
ChatType: "direct",
ConversationLabel: "alice user id:U1",
SenderName: "Alice",
SenderId: "U1",
SenderUsername: "alice",
Provider: "discord",
Surface: "discord",
MessageSid: "m1",
OriginatingChannel: "discord",
OriginatingTo: "channel:c1",
CommandAuthorized: true,
});
await processDiscordMessage(messageCtx);
expect(inboundCtxCapture.ctx).toBeTruthy();
expectChannelInboundContextContract(inboundCtxCapture.ctx!);
expectChannelInboundContextContract(ctx);
});
it("keeps Signal inbound context finalized", async () => {

View File

@@ -16,16 +16,12 @@ type ResolveOwningPluginIdsForProvider =
type ResolveNonBundledProviderPluginIds =
typeof import("../providers.js").resolveNonBundledProviderPluginIds;
const resolvePluginProvidersMock = vi.hoisted(() =>
vi.fn<ResolvePluginProviders>((_) => uniqueProviderContractProviders),
);
const resolvePluginProvidersMock = vi.hoisted(() => vi.fn<ResolvePluginProviders>());
const resolveOwningPluginIdsForProviderMock = vi.hoisted(() =>
vi.fn<ResolveOwningPluginIdsForProvider>((params) =>
resolveProviderContractPluginIdsForProvider(params.provider),
),
vi.fn<ResolveOwningPluginIdsForProvider>(),
);
const resolveNonBundledProviderPluginIdsMock = vi.hoisted(() =>
vi.fn<ResolveNonBundledProviderPluginIds>((_) => [] as string[]),
vi.fn<ResolveNonBundledProviderPluginIds>(),
);
vi.mock("../providers.js", () => ({

View File

@@ -12,7 +12,7 @@ import perplexityPlugin from "../../../extensions/perplexity/index.js";
import xaiPlugin from "../../../extensions/xai/index.js";
import zaiPlugin from "../../../extensions/zai/index.js";
import { createCapturedPluginRegistration } from "../captured-registration.js";
import { resolvePluginProviders } from "../providers.js";
import { loadPluginManifestRegistry } from "../manifest-registry.js";
import type {
ImageGenerationProviderPlugin,
MediaUnderstandingProviderPlugin,
@@ -99,19 +99,76 @@ export const providerContractRegistry: ProviderContractEntry[] = buildCapability
select: () => [],
});
const loadedBundledProviderRegistry: ProviderContractEntry[] = resolvePluginProviders({
bundledProviderAllowlistCompat: true,
bundledProviderVitestCompat: true,
cache: false,
activate: false,
})
.filter((provider): provider is ProviderPlugin & { pluginId: string } =>
Boolean(provider.pluginId),
)
.map((provider) => ({
pluginId: provider.pluginId,
provider,
}));
const bundledProviderContractPluginLoaders: Record<
string,
() => Promise<{ default: RegistrablePlugin }>
> = {
"amazon-bedrock": () => import("../../../extensions/amazon-bedrock/index.js"),
anthropic: () => import("../../../extensions/anthropic/index.js"),
byteplus: () => import("../../../extensions/byteplus/index.js"),
chutes: () => import("../../../extensions/chutes/index.js"),
"cloudflare-ai-gateway": () => import("../../../extensions/cloudflare-ai-gateway/index.js"),
"copilot-proxy": () => import("../../../extensions/copilot-proxy/index.js"),
"github-copilot": () => import("../../../extensions/github-copilot/index.js"),
google: () => import("../../../extensions/google/index.js"),
huggingface: () => import("../../../extensions/huggingface/index.js"),
kilocode: () => import("../../../extensions/kilocode/index.js"),
kimi: () => import("../../../extensions/kimi-coding/index.js"),
minimax: () => import("../../../extensions/minimax/index.js"),
mistral: () => import("../../../extensions/mistral/index.js"),
modelstudio: () => import("../../../extensions/modelstudio/index.js"),
moonshot: () => import("../../../extensions/moonshot/index.js"),
nvidia: () => import("../../../extensions/nvidia/index.js"),
ollama: () => import("../../../extensions/ollama/index.js"),
openai: () => import("../../../extensions/openai/index.js"),
opencode: () => import("../../../extensions/opencode/index.js"),
"opencode-go": () => import("../../../extensions/opencode-go/index.js"),
openrouter: () => import("../../../extensions/openrouter/index.js"),
qianfan: () => import("../../../extensions/qianfan/index.js"),
"qwen-portal-auth": () => import("../../../extensions/qwen-portal-auth/index.js"),
sglang: () => import("../../../extensions/sglang/index.js"),
synthetic: () => import("../../../extensions/synthetic/index.js"),
together: () => import("../../../extensions/together/index.js"),
venice: () => import("../../../extensions/venice/index.js"),
"vercel-ai-gateway": () => import("../../../extensions/vercel-ai-gateway/index.js"),
vllm: () => import("../../../extensions/vllm/index.js"),
volcengine: () => import("../../../extensions/volcengine/index.js"),
xai: () => import("../../../extensions/xai/index.js"),
xiaomi: () => import("../../../extensions/xiaomi/index.js"),
zai: () => import("../../../extensions/zai/index.js"),
};
async function loadBundledProviderContractPlugins(): Promise<RegistrablePlugin[]> {
const bundledProviderPluginIds = loadPluginManifestRegistry({})
.plugins.filter((plugin) => plugin.origin === "bundled" && plugin.providers.length > 0)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
const modules = await Promise.all(
bundledProviderPluginIds.map((pluginId) => {
const load = bundledProviderContractPluginLoaders[pluginId];
if (!load) {
throw new Error(`missing bundled provider contract loader for ${pluginId}`);
}
return load();
}),
);
return modules.map((mod, index) => {
const plugin = mod.default as RegistrablePlugin | undefined;
if (!plugin) {
throw new Error(
`bundled provider contract plugin missing default export for ${bundledProviderPluginIds[index]}`,
);
}
return plugin;
});
}
const loadedBundledProviderRegistry: ProviderContractEntry[] = buildCapabilityContractRegistry({
plugins: await loadBundledProviderContractPlugins(),
select: (captured) => captured.providers,
});
providerContractRegistry.splice(
0,

View File

@@ -1,8 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ProviderPlugin } from "../types.js";
import { providerContractPluginIds, uniqueProviderContractProviders } from "./registry.js";
const resolvePluginProvidersMock = vi.fn();
const resolvePluginProvidersMock = vi.hoisted(() => vi.fn());
vi.mock("../providers.js", () => ({
resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args),
@@ -12,6 +11,8 @@ let buildProviderPluginMethodChoice: typeof import("../provider-wizard.js").buil
let resolveProviderModelPickerEntries: typeof import("../provider-wizard.js").resolveProviderModelPickerEntries;
let resolveProviderPluginChoice: typeof import("../provider-wizard.js").resolveProviderPluginChoice;
let resolveProviderWizardOptions: typeof import("../provider-wizard.js").resolveProviderWizardOptions;
let contractProviders: ProviderPlugin[] = [];
let providerContractPluginIds: string[] = [];
function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) {
const values: string[] = [];
@@ -72,6 +73,23 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) {
describe("provider wizard contract", () => {
beforeEach(async () => {
vi.resetModules();
const providersModule =
await vi.importActual<typeof import("../providers.js")>("../providers.js");
contractProviders = providersModule.resolvePluginProviders({
bundledProviderAllowlistCompat: true,
bundledProviderVitestCompat: true,
env: {
...process.env,
VITEST: process.env.VITEST || "1",
},
cache: false,
activate: false,
});
providerContractPluginIds = [
...new Set(
contractProviders.map((provider) => provider.pluginId).filter(Boolean) as string[],
),
].toSorted((left, right) => left.localeCompare(right));
({
buildProviderPluginMethodChoice,
resolveProviderModelPickerEntries,
@@ -79,7 +97,7 @@ describe("provider wizard contract", () => {
resolveProviderWizardOptions,
} = await import("../provider-wizard.js"));
resolvePluginProvidersMock.mockReset();
resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders);
resolvePluginProvidersMock.mockReturnValue(contractProviders);
});
it("exposes every registered provider setup choice through the shared wizard layer", () => {
@@ -98,7 +116,7 @@ describe("provider wizard contract", () => {
expect(
options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)),
).toEqual(resolveExpectedWizardChoiceValues(uniqueProviderContractProviders));
).toEqual(resolveExpectedWizardChoiceValues(contractProviders));
expect(options.map((option) => option.value)).toEqual([
...new Set(options.map((option) => option.value)),
]);
@@ -107,7 +125,7 @@ describe("provider wizard contract", () => {
it("round-trips every shared wizard choice back to its provider and auth method", () => {
for (const option of resolveProviderWizardOptions({ config: {}, env: process.env })) {
const resolved = resolveProviderPluginChoice({
providers: uniqueProviderContractProviders,
providers: contractProviders,
choice: option.value,
});
expect(resolved).not.toBeNull();
@@ -121,10 +139,10 @@ describe("provider wizard contract", () => {
expect(
entries.map((entry) => entry.value).toSorted((left, right) => left.localeCompare(right)),
).toEqual(resolveExpectedModelPickerValues(uniqueProviderContractProviders));
).toEqual(resolveExpectedModelPickerValues(contractProviders));
for (const entry of entries) {
const resolved = resolveProviderPluginChoice({
providers: uniqueProviderContractProviders,
providers: contractProviders,
choice: entry.value,
});
expect(resolved).not.toBeNull();