fix(voice-call): align plugin manifest schema with runtime config fields (#38892)

Co-authored-by: giumex <giuliano.messina@gmail.com>
This commit is contained in:
Peter Steinberger
2026-03-07 19:49:58 +00:00
parent 43b36bfe8c
commit cf290e31bd
3 changed files with 93 additions and 1 deletions

View File

@@ -265,6 +265,7 @@ Docs: https://docs.openclaw.ai
- Config/validation log sanitization: sanitize config-validation issue paths/messages before logging so control characters and ANSI escape sequences cannot inject misleading terminal output from crafted config content. (#39116) Thanks @powermaster888.
- Agents/compaction counter accuracy: count successful overflow-triggered auto-compactions (`willRetry=true`) in the compaction counter while still excluding aborted/no-result events, so `/status` reflects actual safeguard compaction activity. (#39123) Thanks @MumuTW.
- Gateway/chat delta ordering: flush buffered assistant deltas before emitting tool `start` events so pre-tool text is delivered to Control UI before tool cards, avoiding transient text/tool ordering artifacts in streaming. (#39128) Thanks @0xtangping.
- Voice-call plugin schema parity: add missing manifest `configSchema` fields (`webhookSecurity`, `streaming.preStartTimeoutMs|maxPendingConnections|maxPendingConnectionsPerIp|maxConnections`, `staleCallReaperSeconds`) so gateway AJV validation accepts already-supported runtime config instead of failing with `additionalProperties` errors. (#38892) Thanks @giumex.
## 2026.3.2

View File

@@ -249,6 +249,10 @@
"type": "integer",
"minimum": 1
},
"staleCallReaperSeconds": {
"type": "integer",
"minimum": 0
},
"silenceTimeoutMs": {
"type": "integer",
"minimum": 1
@@ -313,6 +317,27 @@
}
}
},
"webhookSecurity": {
"type": "object",
"additionalProperties": false,
"properties": {
"allowedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"trustForwardingHeaders": {
"type": "boolean"
},
"trustedProxyIPs": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"streaming": {
"type": "object",
"additionalProperties": false,
@@ -341,6 +366,22 @@
},
"streamPath": {
"type": "string"
},
"preStartTimeoutMs": {
"type": "integer",
"minimum": 1
},
"maxPendingConnections": {
"type": "integer",
"minimum": 1
},
"maxPendingConnectionsPerIp": {
"type": "integer",
"minimum": 1
},
"maxConnections": {
"type": "integer",
"minimum": 1
}
}
},

View File

@@ -37,6 +37,7 @@ describe("config plugin validation", () => {
let badPluginDir = "";
let enumPluginDir = "";
let bluebubblesPluginDir = "";
let voiceCallSchemaPluginDir = "";
const envSnapshot = {
OPENCLAW_STATE_DIR: process.env.OPENCLAW_STATE_DIR,
OPENCLAW_PLUGIN_MANIFEST_CACHE_MS: process.env.OPENCLAW_PLUGIN_MANIFEST_CACHE_MS,
@@ -83,6 +84,24 @@ describe("config plugin validation", () => {
channels: ["bluebubbles"],
schema: { type: "object" },
});
voiceCallSchemaPluginDir = path.join(suiteHome, "voice-call-schema-plugin");
const voiceCallManifestPath = path.join(
process.cwd(),
"extensions",
"voice-call",
"openclaw.plugin.json",
);
const voiceCallManifest = JSON.parse(await fs.readFile(voiceCallManifestPath, "utf-8")) as {
configSchema?: Record<string, unknown>;
};
if (!voiceCallManifest.configSchema) {
throw new Error("voice-call manifest missing configSchema");
}
await writePluginFixture({
dir: voiceCallSchemaPluginDir,
id: "voice-call-schema-fixture",
schema: voiceCallManifest.configSchema,
});
process.env.OPENCLAW_STATE_DIR = path.join(suiteHome, ".openclaw");
process.env.OPENCLAW_PLUGIN_MANIFEST_CACHE_MS = "10000";
clearPluginManifestRegistryCache();
@@ -91,7 +110,7 @@ describe("config plugin validation", () => {
validateInSuite({
plugins: {
enabled: false,
load: { paths: [badPluginDir, bluebubblesPluginDir] },
load: { paths: [badPluginDir, bluebubblesPluginDir, voiceCallSchemaPluginDir] },
},
});
});
@@ -229,6 +248,37 @@ describe("config plugin validation", () => {
}
});
it("accepts voice-call webhookSecurity and streaming guard config fields", async () => {
const res = validateInSuite({
agents: { list: [{ id: "pi" }] },
plugins: {
enabled: true,
load: { paths: [voiceCallSchemaPluginDir] },
entries: {
"voice-call-schema-fixture": {
config: {
provider: "twilio",
webhookSecurity: {
allowedHosts: ["voice.example.com"],
trustForwardingHeaders: false,
trustedProxyIPs: ["127.0.0.1"],
},
streaming: {
enabled: true,
preStartTimeoutMs: 5000,
maxPendingConnections: 16,
maxPendingConnectionsPerIp: 4,
maxConnections: 64,
},
staleCallReaperSeconds: 180,
},
},
},
},
});
expect(res.ok).toBe(true);
});
it("accepts known plugin ids and valid channel/heartbeat enums", async () => {
const res = validateInSuite({
agents: {