


























@@ -11,6 +11,7 @@ import {
1111resolveSubagentToolPolicyForSession,
1212} from "./pi-tools.policy.js";
1313import { createStubTool } from "./test-helpers/pi-tool-stubs.js";
14+import { providerAliasCases } from "./test-helpers/provider-alias-cases.js";
14151516describe("pi-tools.policy", () => {
1617it("treats * in allow as allow-all", () => {
@@ -240,6 +241,136 @@ describe("resolveSubagentToolPolicy depth awareness", () => {
240241});
241242242243describe("resolveEffectiveToolPolicy", () => {
244+it.each(providerAliasCases)(
245+"matches provider alias %s to canonical tools.byProvider key %s",
246+(alias, canonical) => {
247+const cfg = {
248+tools: {
249+byProvider: {
250+[canonical]: { deny: ["exec"] },
251+},
252+},
253+} as unknown as OpenClawConfig;
254+255+const result = resolveEffectiveToolPolicy({ config: cfg, modelProvider: alias });
256+257+expect(result.globalProviderPolicy).toEqual({ deny: ["exec"] });
258+},
259+);
260+261+it.each(providerAliasCases)(
262+"matches provider alias %s to canonical model-scoped tools.byProvider key %s",
263+(alias, canonical) => {
264+const cfg = {
265+tools: {
266+byProvider: {
267+[`${canonical}/claude-sonnet`]: { deny: ["exec"] },
268+},
269+},
270+} as unknown as OpenClawConfig;
271+272+const result = resolveEffectiveToolPolicy({
273+config: cfg,
274+modelProvider: alias,
275+modelId: "claude-sonnet",
276+});
277+278+expect(result.globalProviderPolicy).toEqual({ deny: ["exec"] });
279+},
280+);
281+282+it("prefers canonical tools.byProvider policy when alias keys collide after normalization", () => {
283+const aliasFirst = {
284+tools: {
285+byProvider: {
286+bedrock: { deny: ["read"] },
287+"amazon-bedrock": { deny: ["exec"] },
288+},
289+},
290+} as unknown as OpenClawConfig;
291+const canonicalFirst = {
292+tools: {
293+byProvider: {
294+"amazon-bedrock": { deny: ["exec"] },
295+bedrock: { deny: ["read"] },
296+},
297+},
298+} as unknown as OpenClawConfig;
299+300+expect(
301+resolveEffectiveToolPolicy({ config: aliasFirst, modelProvider: "bedrock" })
302+.globalProviderPolicy,
303+).toEqual({ deny: ["exec"] });
304+expect(
305+resolveEffectiveToolPolicy({ config: canonicalFirst, modelProvider: "bedrock" })
306+.globalProviderPolicy,
307+).toEqual({ deny: ["exec"] });
308+});
309+310+it("prefers canonical model-scoped tools.byProvider policy when alias keys collide", () => {
311+const aliasFirst = {
312+tools: {
313+byProvider: {
314+"bedrock/claude-sonnet": { deny: ["read"] },
315+"amazon-bedrock/claude-sonnet": { deny: ["exec"] },
316+},
317+},
318+} as unknown as OpenClawConfig;
319+const canonicalFirst = {
320+tools: {
321+byProvider: {
322+"amazon-bedrock/claude-sonnet": { deny: ["exec"] },
323+"bedrock/claude-sonnet": { deny: ["read"] },
324+},
325+},
326+} as unknown as OpenClawConfig;
327+const params = { modelProvider: "bedrock", modelId: "claude-sonnet" };
328+329+expect(
330+resolveEffectiveToolPolicy({ config: aliasFirst, ...params }).globalProviderPolicy,
331+).toEqual({ deny: ["exec"] });
332+expect(
333+resolveEffectiveToolPolicy({ config: canonicalFirst, ...params }).globalProviderPolicy,
334+).toEqual({ deny: ["exec"] });
335+});
336+337+it("keeps slash-containing modelId scoped to the selected provider", () => {
338+const cfg = {
339+tools: {
340+byProvider: {
341+"anthropic/claude-sonnet": { deny: ["exec"] },
342+"openrouter/anthropic/claude-sonnet": { deny: ["read"] },
343+},
344+},
345+} as unknown as OpenClawConfig;
346+347+expect(
348+resolveEffectiveToolPolicy({
349+config: cfg,
350+modelProvider: "openrouter",
351+modelId: "anthropic/claude-sonnet",
352+}).globalProviderPolicy,
353+).toEqual({ deny: ["read"] });
354+});
355+356+it("does not let slash-containing modelId select another provider policy", () => {
357+const cfg = {
358+tools: {
359+byProvider: {
360+"anthropic/claude-sonnet": { deny: ["exec"] },
361+},
362+},
363+} as unknown as OpenClawConfig;
364+365+expect(
366+resolveEffectiveToolPolicy({
367+config: cfg,
368+modelProvider: "openrouter",
369+modelId: "anthropic/claude-sonnet",
370+}).globalProviderPolicy,
371+).toBeUndefined();
372+});
373+243374it("implicitly re-exposes exec and process when tools.exec is configured", () => {
244375const cfg = {
245376tools: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。