fix(qqbot): allow extension fields in channel config schema (#64075)

* fix(qqbot): allow extension fields in channel config schema

Use passthrough() on QQBotConfigSchema, QQBotAccountSchema, and
QQBotStreamingSchema so third-party builds that share the qqbot
channel id can add custom fields without triggering
"must NOT have additional properties" validation errors.

tts and stt sub-schemas remain strict to preserve typo detection
for those sensitive fields.

* Update extensions/qqbot/openclaw.plugin.json

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* chore(qqbot): update changelog for config schema passthrough

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Mingkuan
2026-04-10 17:01:00 +08:00
committed by GitHub
parent 3b6500ca20
commit 005b629b6d
4 changed files with 82 additions and 46 deletions

View File

@@ -27,6 +27,10 @@ Docs: https://docs.openclaw.ai
- Windows/exec: settle supervisor waits from child exit state after stdout and stderr drain even when `close` never arrives, so CLI commands stop hanging or dying with forced `SIGKILL` on Windows. (#64072) Thanks @obviyus.
- Browser/sandbox: prevent sandbox browser CDP startup hangs by recreating containers when the browser security hash changes and by waiting on the correct sandbox browser lifecycle. (#62873) Thanks @Syysean.
- iMessage/self-chat: distinguish normal DM outbound rows from true self-chat using `destination_caller_id` plus chat participants, while preserving multi-handle self-chat aliases so outbound DM replies stop looping back as inbound messages. (#61619) Thanks @neeravmakwana.
- QQBot/streaming: make block streaming configurable per QQ bot account via `streaming.mode` (`"partial"` | `"off"`, default `"partial"`) instead of hardcoding it off, so responses can be delivered incrementally. (#63746)
- QQBot/config: allow extra fields in `channels.qqbot` and `channels.qqbot.accounts.*` so extended qqbot builds can add new config options without gateway startup failing on schema validation. (#64075) Thanks @WideLee.
- Dreaming/gateway: require `operator.admin` for persistent `/dreaming on|off` changes and treat missing gateway client scopes as unprivileged instead of silently allowing config writes. (#63872) Thanks @mbelinky.
- Matrix/multi-account: keep room-level `account` scoping, inherited room overrides, and implicit account selection consistent across top-level default auth, named accounts, and cached-credential env setups. (#58449) thanks @Daanvdplas and @gumadeiras.
- Gateway/pairing: prefer explicit QR bootstrap auth over earlier Tailscale auth classification so iOS `/pair qr` silent bootstrap pairing does not fall through to `pairing required`. (#59232) Thanks @ngutman.
- Browser/control: auto-generate browser-control auth tokens for `none` and `trusted-proxy` modes, and route browser auth/profile/doctor helpers through the public browser plugin facades. (#63280, #63957) Thanks @pgondhi987.
- Browser/act: centralize `/act` request normalization and execution dispatch while adding stable machine-readable route-level error codes for invalid requests, selector misuse, evaluate-disabled gating, target mismatch, and existing-session unsupported actions. (#63977) Thanks @joshavant.

View File

@@ -7,7 +7,7 @@
"skills": ["./skills"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"additionalProperties": true,
"$defs": {
"audioFormatPolicy": {
"type": "object",
@@ -77,7 +77,7 @@
},
"account": {
"type": "object",
"additionalProperties": false,
"additionalProperties": true,
"properties": {
"enabled": { "type": "boolean" },
"name": { "type": "string" },
@@ -102,15 +102,22 @@
"enum": ["doc", "hot-reload"]
},
"streaming": {
"type": "object",
"additionalProperties": false,
"properties": {
"mode": {
"type": "string",
"enum": ["off", "partial"],
"default": "partial"
"anyOf": [
{
"type": "boolean"
},
{
"type": "object",
"additionalProperties": true,
"properties": {
"mode": {
"type": "string",
"enum": ["off", "partial"],
"default": "partial"
}
}
}
}
]
}
}
}
@@ -141,15 +148,22 @@
"enum": ["doc", "hot-reload"]
},
"streaming": {
"type": "object",
"additionalProperties": false,
"properties": {
"mode": {
"type": "string",
"enum": ["off", "partial"],
"default": "partial"
"anyOf": [
{
"type": "boolean"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"mode": {
"type": "string",
"enum": ["off", "partial"],
"default": "partial"
}
}
}
}
]
},
"accounts": {
"type": "object",

View File

@@ -42,11 +42,15 @@ const QQBotSttSchema = z
.optional();
const QQBotStreamingSchema = z
.object({
/** "partial" (default) enables block streaming; "off" disables it. */
mode: z.enum(["off", "partial"]).default("partial"),
})
.strict()
.union([
z.boolean(),
z
.object({
/** "partial" (default) enables block streaming; "off" disables it. */
mode: z.enum(["off", "partial"]).default("partial"),
})
.passthrough(),
])
.optional();
const QQBotAccountSchema = z
@@ -66,12 +70,12 @@ const QQBotAccountSchema = z
upgradeMode: z.enum(["doc", "hot-reload"]).optional(),
streaming: QQBotStreamingSchema,
})
.strict();
.passthrough();
export const QQBotConfigSchema = QQBotAccountSchema.extend({
tts: QQBotTtsSchema,
stt: QQBotSttSchema,
accounts: z.object({}).catchall(QQBotAccountSchema).optional(),
accounts: z.object({}).catchall(QQBotAccountSchema.passthrough()).optional(),
defaultAccount: z.string().optional(),
});
}).passthrough();
export const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotConfigSchema);

View File

@@ -9324,16 +9324,23 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
enum: ["doc", "hot-reload"],
},
streaming: {
type: "object",
properties: {
mode: {
default: "partial",
type: "string",
enum: ["off", "partial"],
anyOf: [
{
type: "boolean",
},
},
required: ["mode"],
additionalProperties: false,
{
type: "object",
properties: {
mode: {
default: "partial",
type: "string",
enum: ["off", "partial"],
},
},
required: ["mode"],
additionalProperties: {},
},
],
},
tts: {
type: "object",
@@ -9537,26 +9544,33 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
enum: ["doc", "hot-reload"],
},
streaming: {
type: "object",
properties: {
mode: {
default: "partial",
type: "string",
enum: ["off", "partial"],
anyOf: [
{
type: "boolean",
},
},
required: ["mode"],
additionalProperties: false,
{
type: "object",
properties: {
mode: {
default: "partial",
type: "string",
enum: ["off", "partial"],
},
},
required: ["mode"],
additionalProperties: {},
},
],
},
},
additionalProperties: false,
additionalProperties: {},
},
},
defaultAccount: {
type: "string",
},
},
additionalProperties: false,
additionalProperties: {},
},
},
{