fix: clarify gated core tool warnings

This commit is contained in:
Peter Steinberger
2026-03-13 15:37:21 +00:00
parent 55e79adf69
commit 394fd87c2c
3 changed files with 53 additions and 3 deletions

View File

@@ -45,6 +45,31 @@ describe("tool-policy-pipeline", () => {
expect(warnings[0]).toContain("unknown entries (wat)");
});
test("warns gated core tools as unavailable instead of plugin-only unknowns", () => {
const warnings: string[] = [];
const tools = [{ name: "exec" }] as unknown as DummyTool[];
applyToolPolicyPipeline({
// oxlint-disable-next-line typescript/no-explicit-any
tools: tools as any,
// oxlint-disable-next-line typescript/no-explicit-any
toolMeta: () => undefined,
warn: (msg) => warnings.push(msg),
steps: [
{
policy: { allow: ["apply_patch"] },
label: "tools.profile (coding)",
stripPluginOnlyAllowlist: true,
},
],
});
expect(warnings.length).toBe(1);
expect(warnings[0]).toContain("unknown entries (apply_patch)");
expect(warnings[0]).toContain(
"shipped core tools but unavailable in the current runtime/provider/model/config",
);
expect(warnings[0]).not.toContain("unless the plugin is enabled");
});
test("applies allowlist filtering when core tools are explicitly listed", () => {
const tools = [{ name: "exec" }, { name: "process" }] as unknown as DummyTool[];
const filtered = applyToolPolicyPipeline({

View File

@@ -1,5 +1,6 @@
import { filterToolsByPolicy } from "./pi-tools.policy.js";
import type { AnyAgentTool } from "./pi-tools.types.js";
import { isKnownCoreToolId } from "./tool-catalog.js";
import {
buildPluginToolGroups,
expandPolicyWithPluginGroups,
@@ -91,9 +92,15 @@ export function applyToolPolicyPipeline(params: {
const resolved = stripPluginOnlyAllowlist(policy, pluginGroups, coreToolNames);
if (resolved.unknownAllowlist.length > 0) {
const entries = resolved.unknownAllowlist.join(", ");
const suffix = resolved.strippedAllowlist
? "Ignoring allowlist so core tools remain available. Use tools.alsoAllow for additive plugin tool enablement."
: "These entries won't match any tool unless the plugin is enabled.";
const gatedCoreEntries = resolved.unknownAllowlist.filter((entry) =>
isKnownCoreToolId(entry),
);
const otherEntries = resolved.unknownAllowlist.filter((entry) => !isKnownCoreToolId(entry));
const suffix = describeUnknownAllowlistSuffix({
strippedAllowlist: resolved.strippedAllowlist,
hasGatedCoreEntries: gatedCoreEntries.length > 0,
hasOtherEntries: otherEntries.length > 0,
});
params.warn(
`tools: ${step.label} allowlist contains unknown entries (${entries}). ${suffix}`,
);
@@ -106,3 +113,20 @@ export function applyToolPolicyPipeline(params: {
}
return filtered;
}
function describeUnknownAllowlistSuffix(params: {
strippedAllowlist: boolean;
hasGatedCoreEntries: boolean;
hasOtherEntries: boolean;
}): string {
const preface = params.strippedAllowlist
? "Ignoring allowlist so core tools remain available."
: "";
const detail =
params.hasGatedCoreEntries && params.hasOtherEntries
? "Some entries are shipped core tools but unavailable in the current runtime/provider/model/config; other entries won't match any tool unless the plugin is enabled."
: params.hasGatedCoreEntries
? "These entries are shipped core tools but unavailable in the current runtime/provider/model/config."
: "These entries won't match any tool unless the plugin is enabled.";
return preface ? `${preface} ${detail}` : detail;
}