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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user