惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
L
LangChain Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
J
Java Code Geeks
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 叶小钗
小众软件
小众软件
博客园 - Franky
D
Docker
Google DeepMind News
Google DeepMind News
Microsoft Azure Blog
Microsoft Azure Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
宝玉的分享
宝玉的分享
C
Check Point Blog
B
Blog
V
V2EX
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
The Cloudflare Blog
博客园 - 聂微东
博客园_首页
Engineering at Meta
Engineering at Meta

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix: honor voice agent tool allowlist · openclaw/openclaw...
steipete · 2026-05-10 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai

2020
2121

### Fixes

2222
23+

- Voice/Ollama: honor routed voice agent `tools.allow` for classic embedded voice responses, including empty allowlists, so no-tool Ollama agents do not receive tool schemas. Fixes #79506. Thanks @donkeykong91.

2324

- Gateway: reread config from disk after the first in-process restart loop startup, preventing SIGUSR1 restarts from reusing a stale startup snapshot and dropping config written after boot. Fixes #79947. Thanks @TheLevti.

2425

- Codex app-server: deliver native image-generation outputs from Codex `savedPath` events as reply media, so blank-text image generation turns still attach the generated file. Thanks @keshavbotagent.

2526

- Network/SSRF: keep pinned automatic DNS lookups on IPv4 when dual-stack hosts also publish AAAA records, and treat `EADDRNOTAVAIL` as a transient gateway network failure instead of a fatal crash. Fixes #80078. Thanks @takamasa-aiso.

Original file line numberDiff line numberDiff line change

@@ -21,6 +21,7 @@ type EmbeddedAgentArgs = {

2121

agentId?: string;

2222

workspaceDir?: string;

2323

sessionFile?: string;

24+

toolsAllow?: string[];

2425

};

2526
2627

function createAgentRuntime(payloads: Array<Record<string, unknown>>) {

@@ -331,4 +332,39 @@ describe("generateVoiceResponse", () => {

331332

expect(args.workspaceDir).toBe("/tmp/openclaw/workspace/voice");

332333

expect(args.sessionFile).toBe("/tmp/openclaw/voice/sessions/session.jsonl");

333334

});

335+
336+

it("passes the routed voice agent explicit tool allowlist to the embedded run", async () => {

337+

const { runtime, runEmbeddedPiAgent } = createAgentRuntime([

338+

{ text: '{"spoken":"No tools needed."}' },

339+

]);

340+

const coreConfig = {

341+

agents: {

342+

list: [

343+

{

344+

id: "voice",

345+

tools: { allow: [] },

346+

},

347+

],

348+

},

349+

} as CoreConfig;

350+
351+

const result = await generateVoiceResponse({

352+

voiceConfig: VoiceCallConfigSchema.parse({

353+

agentId: "voice",

354+

responseModel: "ollama/qwen2.5:1.5b",

355+

responseTimeoutMs: 5000,

356+

}),

357+

coreConfig,

358+

agentRuntime: runtime,

359+

callId: "call-123",

360+

from: "+15550001111",

361+

transcript: [],

362+

userMessage: "hello there",

363+

});

364+
365+

expect(result.text).toBe("No tools needed.");

366+

const args = requireEmbeddedAgentArgs(runEmbeddedPiAgent);

367+

expect(args.agentId).toBe("voice");

368+

expect(args.toolsAllow).toStrictEqual([]);

369+

});

334370

});

Original file line numberDiff line numberDiff line change

@@ -41,6 +41,34 @@ type VoiceResponsePayload = {

4141

isReasoning?: boolean;

4242

};

4343
44+

function isRecord(value: unknown): value is Record<string, unknown> {

45+

return typeof value === "object" && value !== null && !Array.isArray(value);

46+

}

47+
48+

function readExplicitToolsAllow(value: unknown): string[] | undefined {

49+

if (!isRecord(value)) {

50+

return undefined;

51+

}

52+
53+

const allow = value.allow;

54+

if (!Array.isArray(allow)) {

55+

return undefined;

56+

}

57+
58+

return allow.filter((entry): entry is string => typeof entry === "string");

59+

}

60+
61+

function resolveVoiceAgentToolsAllow(config: CoreConfig, agentId: string): string[] | undefined {

62+

const agents = isRecord(config.agents) ? config.agents : undefined;

63+

const list = Array.isArray(agents?.list) ? agents.list : [];

64+

const agent = list.find((entry) => isRecord(entry) && entry.id === agentId);

65+

if (!isRecord(agent)) {

66+

return undefined;

67+

}

68+
69+

return readExplicitToolsAllow(isRecord(agent.tools) ? agent.tools : undefined);

70+

}

71+
4472

const VOICE_SPOKEN_OUTPUT_CONTRACT = [

4573

"Output format requirements:",

4674

'- Return only valid JSON in this exact shape: {"spoken":"..."}',

@@ -212,6 +240,7 @@ export async function generateVoiceResponse(

212240

explicitSessionKey: sessionKey,

213241

});

214242

const agentId = voiceConfig.agentId ?? "main";

243+

const toolsAllow = resolveVoiceAgentToolsAllow(cfg, agentId);

215244
216245

// Resolve paths

217246

const storePath = agentRuntime.session.resolveStorePath(cfg.session?.store, { agentId });

@@ -302,6 +331,7 @@ export async function generateVoiceResponse(

302331

lane: "voice",

303332

extraSystemPrompt,

304333

agentDir,

334+

toolsAllow,

305335

});

306336
307337

const text = extractSpokenTextFromPayloads((result.payloads ?? []) as VoiceResponsePayload[]);