fix(matrix): migrate room allow aliases to enabled (#60690)

* fix(matrix): migrate room allow aliases to enabled

* test(matrix): keep migration coverage on the channel seam

* chore(config): refresh baselines after matrix alias cleanup
This commit is contained in:
Vincent Koc 2026-04-04 14:27:50 +09:00 committed by GitHub
parent 6e0fe1b91e
commit b390591779
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1063 additions and 360 deletions

View File

@ -13821,16 +13821,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.groups.*.allow",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.groups.*.allowBots",
"kind": "channel",
@ -14200,16 +14190,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.rooms.*.allow",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.rooms.*.allowBots",
"kind": "channel",

View File

@ -1704,7 +1704,7 @@
"tags": [
"automation"
],
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.",
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: feishu, googlechat, nostr, msteams, mattermost, nextcloud-talk, matrix, bluebubbles, line, zalo, zalouser, synology-chat, tlon, discord, imessage, irc, qqbot, signal, slack, telegram, twitch, whatsapp.",
"hasChildren": false
},
{
@ -4532,7 +4532,7 @@
"tags": [
"automation"
],
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.",
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: feishu, googlechat, nostr, msteams, mattermost, nextcloud-talk, matrix, bluebubbles, line, zalo, zalouser, synology-chat, tlon, discord, imessage, irc, qqbot, signal, slack, telegram, twitch, whatsapp.",
"hasChildren": false
},
{
@ -15426,6 +15426,16 @@
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.models.*.contextTokens",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.models.*.contextWindow",
"kind": "core",

View File

@ -1703,7 +1703,7 @@
"tags": [
"automation"
],
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.",
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: feishu, googlechat, nostr, msteams, mattermost, nextcloud-talk, matrix, bluebubbles, line, zalo, zalouser, synology-chat, tlon, discord, imessage, irc, qqbot, signal, slack, telegram, twitch, whatsapp.",
"hasChildren": false
},
{
@ -4531,7 +4531,7 @@
"tags": [
"automation"
],
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.",
"help": "Delivery target (\"last\", \"none\", or a channel id). Known channels: feishu, googlechat, nostr, msteams, mattermost, nextcloud-talk, matrix, bluebubbles, line, zalo, zalouser, synology-chat, tlon, discord, imessage, irc, qqbot, signal, slack, telegram, twitch, whatsapp.",
"hasChildren": false
},
{
@ -15425,6 +15425,16 @@
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.models.*.contextTokens",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.models.*.contextWindow",
"kind": "core",
@ -41851,16 +41861,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.groups.*.allow",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.groups.*.allowBots",
"kind": "channel",
@ -42230,16 +42230,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.rooms.*.allow",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.rooms.*.allowBots",
"kind": "channel",
@ -65406,6 +65396,120 @@
],
"label": "@openclaw/memory-core Config",
"help": "Plugin-defined config payload for memory-core.",
"hasChildren": true
},
{
"path": "plugins.entries.memory-core.config.dreaming",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.memory-core.config.dreaming.frequency",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Frequency",
"help": "Optional cron cadence override for managed dreaming runs.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.limit",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance"
],
"label": "Promotion Limit",
"help": "Maximum short-term candidates promoted per dreaming run (set to 0 to skip promotions).",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minRecallCount",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Recalls",
"help": "Minimum recall count required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minScore",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Score",
"help": "Minimum weighted rank required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minUniqueQueries",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Queries",
"help": "Minimum unique query count required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.mode",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"off",
"core",
"deep",
"rem"
],
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Mode",
"help": "Select dreaming mode: off, core, deep, or rem.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.timezone",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Timezone",
"help": "IANA timezone for the managed dreaming cron schedule.",
"hasChildren": false
},
{
@ -67287,6 +67391,21 @@
],
"label": "@openclaw/openai-provider Config",
"help": "Plugin-defined config payload for openai.",
"hasChildren": true
},
{
"path": "plugins.entries.openai.config.personalityOverlay",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"friendly",
"off"
],
"defaultValue": "friendly",
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
@ -71224,6 +71343,199 @@
"label": "Public Webhook URL",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.enabled",
"kind": "plugin",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Enable Realtime Voice",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.instructions",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Instructions",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.provider",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Voice Provider",
"help": "Uses the first registered realtime voice provider when unset.",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.providers",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Provider Config",
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.providers.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.streamPath",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"storage"
],
"label": "Realtime Stream Path",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools",
"kind": "plugin",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.description",
"kind": "plugin",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.name",
"kind": "plugin",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters",
"kind": "plugin",
"type": "object",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.properties",
"kind": "plugin",
"type": "object",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.required",
"kind": "plugin",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.required.*",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.type",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"object"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.type",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"function"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.responseModel",
"kind": "plugin",
@ -71235,6 +71547,7 @@
"advanced"
],
"label": "Response Model",
"help": "Optional override. Falls back to the runtime default model when unset.",
"hasChildren": false
},
{
@ -71423,21 +71736,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.openaiApiKey",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"security"
],
"label": "OpenAI Realtime API Key",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.preStartTimeoutMs",
"kind": "plugin",
@ -71449,9 +71747,36 @@
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.silenceDurationMs",
"path": "plugins.entries.voice-call.config.streaming.provider",
"kind": "plugin",
"type": "integer",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Streaming Provider",
"help": "Uses the first registered realtime transcription provider when unset.",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.providers",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Streaming Provider Config",
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.streaming.providers.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
@ -71472,76 +71797,6 @@
"label": "Media Stream Path",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.sttModel",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "Realtime STT Model",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.sttProvider",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"openai-realtime"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.vadThreshold",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.stt",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.stt.model",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.stt.provider",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"openai"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.tailscale",
"kind": "plugin",
@ -71839,7 +72094,11 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"tags": [
"advanced",
"media"
],
"label": "TTS Provider Config",
"hasChildren": true
},
{
@ -71994,12 +72253,10 @@
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"media",
"security"
],
"label": "ElevenLabs API Key",
"hasChildren": false
},
{
@ -72025,11 +72282,9 @@
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"url-secret"
],
"label": "ElevenLabs Base URL",
"hasChildren": false
},
{
@ -72049,12 +72304,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"models"
],
"label": "ElevenLabs Model ID",
"tags": [],
"hasChildren": false
},
{
@ -72074,11 +72324,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "ElevenLabs Voice ID",
"tags": [],
"hasChildren": false
},
{
@ -72269,12 +72515,10 @@
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"media",
"security"
],
"label": "OpenAI API Key",
"hasChildren": false
},
{
@ -72307,12 +72551,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"models"
],
"label": "OpenAI TTS Model",
"tags": [],
"hasChildren": false
},
{
@ -72332,11 +72571,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "OpenAI TTS Voice",
"tags": [],
"hasChildren": false
},
{

View File

@ -5731,6 +5731,120 @@
],
"label": "@openclaw/memory-core Config",
"help": "Plugin-defined config payload for memory-core.",
"hasChildren": true
},
{
"path": "plugins.entries.memory-core.config.dreaming",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.memory-core.config.dreaming.frequency",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Frequency",
"help": "Optional cron cadence override for managed dreaming runs.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.limit",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance"
],
"label": "Promotion Limit",
"help": "Maximum short-term candidates promoted per dreaming run (set to 0 to skip promotions).",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minRecallCount",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Recalls",
"help": "Minimum recall count required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minScore",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Score",
"help": "Minimum weighted rank required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.minUniqueQueries",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Promotion Min Queries",
"help": "Minimum unique query count required for automatic promotion.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.mode",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"off",
"core",
"deep",
"rem"
],
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Mode",
"help": "Select dreaming mode: off, core, deep, or rem.",
"hasChildren": false
},
{
"path": "plugins.entries.memory-core.config.dreaming.timezone",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Dreaming Timezone",
"help": "IANA timezone for the managed dreaming cron schedule.",
"hasChildren": false
},
{
@ -7612,6 +7726,21 @@
],
"label": "@openclaw/openai-provider Config",
"help": "Plugin-defined config payload for openai.",
"hasChildren": true
},
{
"path": "plugins.entries.openai.config.personalityOverlay",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"friendly",
"off"
],
"defaultValue": "friendly",
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
@ -11549,6 +11678,199 @@
"label": "Public Webhook URL",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.enabled",
"kind": "plugin",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Enable Realtime Voice",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.instructions",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Instructions",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.provider",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Voice Provider",
"help": "Uses the first registered realtime voice provider when unset.",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.providers",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Realtime Provider Config",
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.providers.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.streamPath",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"storage"
],
"label": "Realtime Stream Path",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools",
"kind": "plugin",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.description",
"kind": "plugin",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.name",
"kind": "plugin",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters",
"kind": "plugin",
"type": "object",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.properties",
"kind": "plugin",
"type": "object",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.required",
"kind": "plugin",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.required.*",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.parameters.type",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"object"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.realtime.tools.*.type",
"kind": "plugin",
"type": "string",
"required": true,
"enumValues": [
"function"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.responseModel",
"kind": "plugin",
@ -11560,6 +11882,7 @@
"advanced"
],
"label": "Response Model",
"help": "Optional override. Falls back to the runtime default model when unset.",
"hasChildren": false
},
{
@ -11748,21 +12071,6 @@
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.openaiApiKey",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"security"
],
"label": "OpenAI Realtime API Key",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.preStartTimeoutMs",
"kind": "plugin",
@ -11774,9 +12082,36 @@
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.silenceDurationMs",
"path": "plugins.entries.voice-call.config.streaming.provider",
"kind": "plugin",
"type": "integer",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Streaming Provider",
"help": "Uses the first registered realtime transcription provider when unset.",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.providers",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced"
],
"label": "Streaming Provider Config",
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.streaming.providers.*",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
@ -11797,76 +12132,6 @@
"label": "Media Stream Path",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.sttModel",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "Realtime STT Model",
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.sttProvider",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"openai-realtime"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.streaming.vadThreshold",
"kind": "plugin",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.stt",
"kind": "plugin",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "plugins.entries.voice-call.config.stt.model",
"kind": "plugin",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.stt.provider",
"kind": "plugin",
"type": "string",
"required": false,
"enumValues": [
"openai"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "plugins.entries.voice-call.config.tailscale",
"kind": "plugin",
@ -12164,7 +12429,11 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"tags": [
"advanced",
"media"
],
"label": "TTS Provider Config",
"hasChildren": true
},
{
@ -12319,12 +12588,10 @@
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"media",
"security"
],
"label": "ElevenLabs API Key",
"hasChildren": false
},
{
@ -12350,11 +12617,9 @@
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"url-secret"
],
"label": "ElevenLabs Base URL",
"hasChildren": false
},
{
@ -12374,12 +12639,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"models"
],
"label": "ElevenLabs Model ID",
"tags": [],
"hasChildren": false
},
{
@ -12399,11 +12659,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "ElevenLabs Voice ID",
"tags": [],
"hasChildren": false
},
{
@ -12594,12 +12850,10 @@
"deprecated": false,
"sensitive": true,
"tags": [
"advanced",
"auth",
"media",
"security"
],
"label": "OpenAI API Key",
"hasChildren": false
},
{
@ -12632,12 +12886,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media",
"models"
],
"label": "OpenAI TTS Model",
"tags": [],
"hasChildren": false
},
{
@ -12657,11 +12906,7 @@
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"media"
],
"label": "OpenAI TTS Voice",
"tags": [],
"hasChildren": false
},
{

View File

@ -37,7 +37,7 @@ describe("MatrixConfigSchema SecretInput", () => {
accessToken: "token",
groups: {
"!room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
},
@ -55,7 +55,7 @@ describe("MatrixConfigSchema SecretInput", () => {
accessToken: "token",
rooms: {
"!room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
},

View File

@ -45,7 +45,6 @@ const matrixRoomSchema = z
.object({
account: z.string().optional(),
enabled: z.boolean().optional(),
allow: z.boolean().optional(),
requireMention: z.boolean().optional(),
allowBots: z.union([z.boolean(), z.literal("mentions")]).optional(),
tools: ToolPolicySchema,

View File

@ -8,6 +8,7 @@ import {
collectMatrixInstallPathWarnings,
formatMatrixLegacyCryptoPreview,
formatMatrixLegacyStatePreview,
matrixDoctor,
runMatrixDoctorSequence,
} from "./doctor.js";
@ -125,4 +126,50 @@ describe("matrix doctor", () => {
});
expect(sequence.changeNotes.join("\n")).toContain("Matrix migration snapshot");
});
it("normalizes legacy Matrix room allow aliases to enabled", () => {
const normalize = matrixDoctor.normalizeCompatibilityConfig;
expect(normalize).toBeDefined();
if (!normalize) {
return;
}
const result = normalize({
cfg: {
channels: {
matrix: {
groups: {
"!ops:example.org": {
allow: true,
},
},
accounts: {
work: {
rooms: {
"!legacy:example.org": {
allow: false,
},
},
},
},
},
},
} as never,
});
expect(result.config.channels?.matrix?.groups?.["!ops:example.org"]).toEqual({
enabled: true,
});
expect(result.config.channels?.matrix?.accounts?.work?.rooms?.["!legacy:example.org"]).toEqual(
{
enabled: false,
},
);
expect(result.changes).toEqual(
expect.arrayContaining([
"Moved channels.matrix.groups.!ops:example.org.allow → channels.matrix.groups.!ops:example.org.enabled (true).",
"Moved channels.matrix.accounts.work.rooms.!legacy:example.org.allow → channels.matrix.accounts.work.rooms.!legacy:example.org.enabled (false).",
]),
);
});
});

View File

@ -1,4 +1,8 @@
import type { ChannelDoctorAdapter } from "openclaw/plugin-sdk/channel-contract";
import {
type ChannelDoctorAdapter,
type ChannelDoctorConfigMutation,
type ChannelDoctorLegacyConfigRule,
} from "openclaw/plugin-sdk/channel-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import {
detectPluginInstallPathIssue,
@ -19,6 +23,161 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function hasLegacyMatrixRoomAllowAlias(value: unknown): boolean {
const room = isRecord(value) ? value : null;
return Boolean(room && typeof room.allow === "boolean");
}
function hasLegacyMatrixRoomMapAllowAliases(value: unknown): boolean {
const rooms = isRecord(value) ? value : null;
return Boolean(rooms && Object.values(rooms).some((room) => hasLegacyMatrixRoomAllowAlias(room)));
}
function hasLegacyMatrixAccountRoomAllowAliases(value: unknown): boolean {
const accounts = isRecord(value) ? value : null;
if (!accounts) {
return false;
}
return Object.values(accounts).some((account) => {
if (!isRecord(account)) {
return false;
}
return (
hasLegacyMatrixRoomMapAllowAliases(account.groups) ||
hasLegacyMatrixRoomMapAllowAliases(account.rooms)
);
});
}
function normalizeMatrixRoomAllowAliases(params: {
rooms: Record<string, unknown>;
pathPrefix: string;
changes: string[];
}): { rooms: Record<string, unknown>; changed: boolean } {
let changed = false;
const nextRooms: Record<string, unknown> = { ...params.rooms };
for (const [roomId, roomValue] of Object.entries(params.rooms)) {
const room = isRecord(roomValue) ? roomValue : null;
if (!room || typeof room.allow !== "boolean") {
continue;
}
const nextRoom = { ...room };
if (typeof nextRoom.enabled !== "boolean") {
nextRoom.enabled = room.allow;
}
delete nextRoom.allow;
nextRooms[roomId] = nextRoom;
changed = true;
params.changes.push(
`Moved ${params.pathPrefix}.${roomId}.allow → ${params.pathPrefix}.${roomId}.enabled (${String(nextRoom.enabled)}).`,
);
}
return { rooms: nextRooms, changed };
}
function normalizeMatrixCompatibilityConfig(cfg: OpenClawConfig): ChannelDoctorConfigMutation {
const channels = isRecord(cfg.channels) ? cfg.channels : null;
const matrix = isRecord(channels?.matrix) ? channels.matrix : null;
if (!matrix) {
return { config: cfg, changes: [] };
}
const changes: string[] = [];
let updatedMatrix: Record<string, unknown> = matrix;
let changed = false;
const normalizeTopLevelRoomScope = (key: "groups" | "rooms") => {
const rooms = isRecord(updatedMatrix[key]) ? updatedMatrix[key] : null;
if (!rooms) {
return;
}
const normalized = normalizeMatrixRoomAllowAliases({
rooms,
pathPrefix: `channels.matrix.${key}`,
changes,
});
if (normalized.changed) {
updatedMatrix = { ...updatedMatrix, [key]: normalized.rooms };
changed = true;
}
};
normalizeTopLevelRoomScope("groups");
normalizeTopLevelRoomScope("rooms");
const accounts = isRecord(updatedMatrix.accounts) ? updatedMatrix.accounts : null;
if (accounts) {
let accountsChanged = false;
const nextAccounts: Record<string, unknown> = { ...accounts };
for (const [accountId, accountValue] of Object.entries(accounts)) {
const account = isRecord(accountValue) ? accountValue : null;
if (!account) {
continue;
}
let nextAccount: Record<string, unknown> = account;
let accountChanged = false;
for (const key of ["groups", "rooms"] as const) {
const rooms = isRecord(nextAccount[key]) ? nextAccount[key] : null;
if (!rooms) {
continue;
}
const normalized = normalizeMatrixRoomAllowAliases({
rooms,
pathPrefix: `channels.matrix.accounts.${accountId}.${key}`,
changes,
});
if (normalized.changed) {
nextAccount = { ...nextAccount, [key]: normalized.rooms };
accountChanged = true;
}
}
if (accountChanged) {
nextAccounts[accountId] = nextAccount;
accountsChanged = true;
}
}
if (accountsChanged) {
updatedMatrix = { ...updatedMatrix, accounts: nextAccounts };
changed = true;
}
}
if (!changed) {
return { config: cfg, changes: [] };
}
return {
config: {
...cfg,
channels: {
...cfg.channels,
matrix: updatedMatrix as OpenClawConfig["channels"]["matrix"],
},
},
changes,
};
}
const MATRIX_LEGACY_CONFIG_RULES: ChannelDoctorLegacyConfigRule[] = [
{
path: ["channels", "matrix", "groups"],
message:
"channels.matrix.groups.<room>.allow is legacy; use channels.matrix.groups.<room>.enabled instead (auto-migrated on load).",
match: hasLegacyMatrixRoomMapAllowAliases,
},
{
path: ["channels", "matrix", "rooms"],
message:
"channels.matrix.rooms.<room>.allow is legacy; use channels.matrix.rooms.<room>.enabled instead (auto-migrated on load).",
match: hasLegacyMatrixRoomMapAllowAliases,
},
{
path: ["channels", "matrix", "accounts"],
message:
"channels.matrix.accounts.<id>.{groups,rooms}.<room>.allow is legacy; use channels.matrix.accounts.<id>.{groups,rooms}.<room>.enabled instead (auto-migrated on load).",
match: hasLegacyMatrixAccountRoomAllowAliases,
},
];
function hasConfiguredMatrixChannel(cfg: OpenClawConfig): boolean {
const channels = cfg.channels as Record<string, unknown> | undefined;
return isRecord(channels?.matrix);
@ -259,6 +418,8 @@ export const matrixDoctor: ChannelDoctorAdapter = {
groupModel: "sender",
groupAllowFromFallbackToAllowFrom: false,
warnOnEmptyGroupSenderAllowlist: true,
legacyConfigRules: MATRIX_LEGACY_CONFIG_RULES,
normalizeCompatibilityConfig: ({ cfg }) => normalizeMatrixCompatibilityConfig(cfg),
runConfigSequence: async ({ cfg, env, shouldRepair }) =>
await runMatrixDoctorSequence({ cfg, env, shouldRepair }),
cleanStaleConfig: async ({ cfg }) => await cleanStaleMatrixPluginConfig(cfg),

View File

@ -476,15 +476,15 @@ describe("resolveMatrixAccount", () => {
matrix: {
groups: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!axis-room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -503,20 +503,20 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.groups).toEqual({
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveMatrixAccount({ cfg, accountId: "axis" }).config.groups).toEqual({
"!axis-room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -529,15 +529,15 @@ describe("resolveMatrixAccount", () => {
accessToken: "default-token",
groups: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -552,20 +552,20 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.groups).toEqual({
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.groups).toEqual({
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -576,15 +576,15 @@ describe("resolveMatrixAccount", () => {
matrix: {
rooms: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!axis-room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -603,20 +603,20 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.rooms).toEqual({
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveMatrixAccount({ cfg, accountId: "axis" }).config.rooms).toEqual({
"!axis-room:example.org": {
allow: true,
enabled: true,
account: "axis",
},
"!unassigned-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -629,15 +629,15 @@ describe("resolveMatrixAccount", () => {
accessToken: "default-token",
rooms: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -652,20 +652,20 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.rooms).toEqual({
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.rooms).toEqual({
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -683,15 +683,15 @@ describe("resolveMatrixAccount", () => {
matrix: {
groups: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
},
@ -700,11 +700,11 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "ops", env }).config.groups).toEqual({
"!ops-room:example.org": {
allow: true,
enabled: true,
account: "ops",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -715,11 +715,11 @@ describe("resolveMatrixAccount", () => {
matrix: {
groups: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -734,7 +734,7 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.groups).toEqual({
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -745,11 +745,11 @@ describe("resolveMatrixAccount", () => {
matrix: {
rooms: {
"!default-room:example.org": {
allow: true,
enabled: true,
account: "default",
},
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -764,7 +764,7 @@ describe("resolveMatrixAccount", () => {
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.rooms).toEqual({
"!shared-room:example.org": {
allow: true,
enabled: true,
},
});
});
@ -775,7 +775,7 @@ describe("resolveMatrixAccount", () => {
matrix: {
groups: {
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {
@ -798,7 +798,7 @@ describe("resolveMatrixAccount", () => {
matrix: {
rooms: {
"!shared-room:example.org": {
allow: true,
enabled: true,
},
},
accounts: {

View File

@ -122,7 +122,7 @@ describe("updateMatrixAccountConfig", () => {
policy: "pairing",
},
groups: {
"!default:example.org": { allow: true },
"!default:example.org": { enabled: true },
},
accounts: {
ops: {
@ -145,14 +145,14 @@ describe("updateMatrixAccountConfig", () => {
},
groupPolicy: "allowlist",
groups: {
"!ops-room:example.org": { allow: true },
"!ops-room:example.org": { enabled: true },
},
rooms: null,
});
expect(updated.channels?.["matrix"]?.dm?.policy).toBe("pairing");
expect(updated.channels?.["matrix"]?.groups).toEqual({
"!default:example.org": { allow: true },
"!default:example.org": { enabled: true },
});
expect(updated.channels?.["matrix"]?.accounts?.ops).toMatchObject({
dm: {
@ -162,7 +162,7 @@ describe("updateMatrixAccountConfig", () => {
},
groupPolicy: "allowlist",
groups: {
"!ops-room:example.org": { allow: true },
"!ops-room:example.org": { enabled: true },
},
});
expect(updated.channels?.["matrix"]?.accounts?.ops?.rooms).toBeUndefined();

View File

@ -39,13 +39,13 @@ describe("resolveMatrixMonitorConfig", () => {
);
const roomsConfig: MatrixRoomsConfig = {
"*": { allow: true },
"*": { enabled: true },
"room:!ops:example.org": {
allow: true,
enabled: true,
users: ["Dana", "user:@Erin:Example.org"],
},
General: {
allow: true,
enabled: true,
},
};
@ -62,13 +62,13 @@ describe("resolveMatrixMonitorConfig", () => {
expect(result.allowFrom).toEqual(["@alice:example.org", "@bob:example.org"]);
expect(result.groupAllowFrom).toEqual(["@carol:example.org"]);
expect(result.roomsConfig).toEqual({
"*": { allow: true },
"*": { enabled: true },
"!ops:example.org": {
allow: true,
enabled: true,
users: ["@dana:example.org", "@erin:example.org"],
},
"!general:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveTargets).toHaveBeenCalledTimes(3);
@ -116,7 +116,7 @@ describe("resolveMatrixMonitorConfig", () => {
groupAllowFrom: ["matrix:@known:example.org"],
roomsConfig: {
"channel:Project X": {
allow: true,
enabled: true,
users: ["matrix:Ghost"],
},
},
@ -174,7 +174,7 @@ describe("resolveMatrixMonitorConfig", () => {
accountId: "ops",
roomsConfig: {
"#allowed:example.org": {
allow: true,
enabled: true,
},
},
runtime,
@ -183,7 +183,7 @@ describe("resolveMatrixMonitorConfig", () => {
expect(result.roomsConfig).toEqual({
"!allowed-room:example.org": {
allow: true,
enabled: true,
},
});
expect(resolveTargets).toHaveBeenCalledWith(

View File

@ -4,9 +4,9 @@ import { resolveMatrixRoomConfig } from "./rooms.js";
describe("resolveMatrixRoomConfig", () => {
it("matches room IDs and aliases, not names", () => {
const rooms = {
"!room:example.org": { allow: true },
"#alias:example.org": { allow: true },
"Project Room": { allow: true },
"!room:example.org": { enabled: true },
"#alias:example.org": { enabled: true },
"Project Room": { enabled: true },
};
const byId = resolveMatrixRoomConfig({
@ -26,7 +26,7 @@ describe("resolveMatrixRoomConfig", () => {
expect(byAlias.matchKey).toBe("#alias:example.org");
const byName = resolveMatrixRoomConfig({
rooms: { "Project Room": { allow: true } },
rooms: { "Project Room": { enabled: true } },
roomId: "!different:example.org",
aliases: [],
});
@ -37,7 +37,7 @@ describe("resolveMatrixRoomConfig", () => {
describe("matchSource classification", () => {
it('returns matchSource="direct" for exact room ID match', () => {
const result = resolveMatrixRoomConfig({
rooms: { "!room:example.org": { allow: true } },
rooms: { "!room:example.org": { enabled: true } },
roomId: "!room:example.org",
aliases: [],
});
@ -47,7 +47,7 @@ describe("resolveMatrixRoomConfig", () => {
it('returns matchSource="direct" for alias match', () => {
const result = resolveMatrixRoomConfig({
rooms: { "#alias:example.org": { allow: true } },
rooms: { "#alias:example.org": { enabled: true } },
roomId: "!room:example.org",
aliases: ["#alias:example.org"],
});
@ -57,7 +57,7 @@ describe("resolveMatrixRoomConfig", () => {
it('returns matchSource="wildcard" for wildcard match', () => {
const result = resolveMatrixRoomConfig({
rooms: { "*": { allow: true } },
rooms: { "*": { enabled: true } },
roomId: "!any:example.org",
aliases: [],
});
@ -67,7 +67,7 @@ describe("resolveMatrixRoomConfig", () => {
it("returns undefined matchSource when no match", () => {
const result = resolveMatrixRoomConfig({
rooms: { "!other:example.org": { allow: true } },
rooms: { "!other:example.org": { enabled: true } },
roomId: "!room:example.org",
aliases: [],
});
@ -78,8 +78,8 @@ describe("resolveMatrixRoomConfig", () => {
it("direct match takes priority over wildcard", () => {
const result = resolveMatrixRoomConfig({
rooms: {
"!room:example.org": { allow: true, systemPrompt: "room-specific" },
"*": { allow: true, systemPrompt: "generic" },
"!room:example.org": { enabled: true, systemPrompt: "room-specific" },
"*": { enabled: true, systemPrompt: "generic" },
},
roomId: "!room:example.org",
aliases: [],
@ -96,7 +96,7 @@ describe("resolveMatrixRoomConfig", () => {
it("wildcard config should NOT be usable to override DM classification", () => {
const result = resolveMatrixRoomConfig({
rooms: { "*": { allow: true, skills: ["general"] } },
rooms: { "*": { enabled: true, skills: ["general"] } },
roomId: "!dm-room:example.org",
aliases: [],
});
@ -108,8 +108,8 @@ describe("resolveMatrixRoomConfig", () => {
it("explicitly configured room should be usable to override DM classification", () => {
const result = resolveMatrixRoomConfig({
rooms: {
"!configured-room:example.org": { allow: true },
"*": { allow: true },
"!configured-room:example.org": { enabled: true },
"*": { enabled: true },
},
roomId: "!configured-room:example.org",
aliases: [],

View File

@ -9,6 +9,11 @@ export type MatrixRoomConfigResolved = {
matchSource?: "direct" | "wildcard";
};
function readLegacyRoomAllowAlias(room: MatrixRoomConfig | undefined): boolean | undefined {
const rawRoom = room as Record<string, unknown> | undefined;
return typeof rawRoom?.allow === "boolean" ? rawRoom.allow : undefined;
}
export function resolveMatrixRoomConfig(params: {
rooms?: Record<string, MatrixRoomConfig>;
roomId: string;
@ -33,7 +38,8 @@ export function resolveMatrixRoomConfig(params: {
wildcardKey: "*",
});
const resolved = matched ?? wildcardEntry;
const allowed = resolved ? resolved.enabled !== false && resolved.allow !== false : false;
const legacyAllow = readLegacyRoomAllowAlias(resolved);
const allowed = resolved ? resolved.enabled !== false && legacyAllow !== false : false;
const matchKey = matchedKey ?? wildcardKey;
const matchSource = matched ? "direct" : wildcardEntry ? "wildcard" : undefined;
return {

View File

@ -308,7 +308,7 @@ describe("matrix onboarding", () => {
},
groupPolicy: "allowlist",
groups: {
"!ops-room:example.org": { allow: true },
"!ops-room:example.org": { enabled: true },
},
});
expect(result.cfg.channels?.["matrix"]?.dm).toBeUndefined();

View File

@ -183,7 +183,7 @@ function setMatrixGroupPolicy(
}
function setMatrixGroupRooms(cfg: CoreConfig, roomKeys: string[], accountId?: string) {
const groups = Object.fromEntries(roomKeys.map((key) => [key, { allow: true }]));
const groups = Object.fromEntries(roomKeys.map((key) => [key, { enabled: true }]));
return updateMatrixAccountConfig(cfg, resolveMatrixOnboardingAccountId(cfg, accountId), {
groups,
rooms: null,

View File

@ -23,10 +23,8 @@ export type MatrixDmConfig = {
export type MatrixRoomConfig = {
/** Restrict this room entry to a specific Matrix account in multi-account setups. */
account?: string;
/** If false, disable the bot in this room (alias for allow: false). */
/** If false, disable the bot in this room. */
enabled?: boolean;
/** Legacy room allow toggle; prefer enabled. */
allow?: boolean;
/** Require mentioning the bot to trigger replies. */
requireMention?: boolean;
/**

View File

@ -6856,9 +6856,6 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
enabled: {
type: "boolean",
},
allow: {
type: "boolean",
},
requireMention: {
type: "boolean",
},
@ -6938,9 +6935,6 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
enabled: {
type: "boolean",
},
allow: {
type: "boolean",
},
requireMention: {
type: "boolean",
},

View File

@ -558,7 +558,7 @@ describe("legacy migrate nested channel enabled aliases", () => {
});
});
it("moves legacy allow toggles into enabled for slack, googlechat, and discord", () => {
it("moves legacy allow toggles into enabled for slack, googlechat, discord, and matrix", () => {
const res = migrateLegacyConfig({
channels: {
slack: {
@ -617,6 +617,22 @@ describe("legacy migrate nested channel enabled aliases", () => {
},
},
},
matrix: {
groups: {
"!ops:example.org": {
allow: false,
},
},
accounts: {
work: {
rooms: {
"!legacy:example.org": {
allow: true,
},
},
},
},
},
},
});
@ -638,6 +654,12 @@ describe("legacy migrate nested channel enabled aliases", () => {
expect(res.changes).toContain(
"Moved channels.discord.accounts.work.guilds.200.channels.help.allow → channels.discord.accounts.work.guilds.200.channels.help.enabled.",
);
expect(res.changes).toContain(
"Moved channels.matrix.groups.!ops:example.org.allow → channels.matrix.groups.!ops:example.org.enabled (false).",
);
expect(res.changes).toContain(
"Moved channels.matrix.accounts.work.rooms.!legacy:example.org.allow → channels.matrix.accounts.work.rooms.!legacy:example.org.enabled (true).",
);
expect(res.config?.channels?.slack?.channels?.ops).toEqual({
enabled: false,
});
@ -647,6 +669,12 @@ describe("legacy migrate nested channel enabled aliases", () => {
expect(res.config?.channels?.discord?.guilds?.["100"]?.channels?.general).toEqual({
enabled: false,
});
expect(res.config?.channels?.matrix?.groups?.["!ops:example.org"]).toEqual({
enabled: false,
});
expect(res.config?.channels?.matrix?.accounts?.work?.rooms?.["!legacy:example.org"]).toEqual({
enabled: true,
});
});
it("drops legacy allow when enabled is already set", () => {