Skip to content

Commit 9c3e5f7

Browse files
committed
fix(tools): allow no-tool llm-task runs
1 parent 0459bff commit 9c3e5f7

4 files changed

Lines changed: 44 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Docs: https://docs.openclaw.ai
118118
- Active Memory: preserve the target agent context when building embedded recall plugin tools so `memory_search` and `memory_get` stay available for explicit recall sessions. Fixes #76343. Thanks @Countermarch.
119119
- Plugins/externalization: repair missing configured plugin installs from npm by default, reserve ClawHub downloads for explicit `clawhubSpec` metadata, and cover agent-runtime/env-selected plugin repair. Thanks @vincentkoc.
120120
- Plugins/install: allow official catalog-matched npm channel plugins such as Feishu to pass the trusted install scanner path while keeping spoofed package names blocked. Thanks @vincentkoc.
121+
- Tools/llm-task: keep JSON-only embedded model runs from tripping inherited tool allowlists when tools are intentionally disabled, while preserving runtime `toolsAllow` failures. Fixes #74019. Thanks @amknight.
121122
- Tools/profiles: make `tools.profile: "full"` grant all tools including optional plugin tools such as browser, so the full profile no longer silently drops plugin-provided tools that require an explicit allowlist entry. Fixes #76507. Thanks @amknight.
122123
- Feishu: keep timeout env parsing separate from the HTTP client wrapper so package security scans no longer report a false env-harvesting hit during install. Thanks @vincentkoc.
123124
- Upgrade/config: validate configured web-search providers and statically suppressed model/provider pairs against the active plugin set at config load, so stale plugin state fails loud before runtime fallback.

src/agents/pi-embedded-runner/run/attempt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ function collectAttemptExplicitToolAllowlistSources(params: {
697697
{ label: "group tools.allow", allow: groupPolicy?.allow },
698698
{ label: "sandbox tools.allow", allow: params.sandboxToolPolicy?.allow },
699699
{ label: "subagent tools.allow", allow: subagentPolicy?.allow },
700-
{ label: "runtime toolsAllow", allow: params.toolsAllow },
700+
{ label: "runtime toolsAllow", allow: params.toolsAllow, enforceWhenToolsDisabled: true },
701701
]);
702702
}
703703

src/agents/tool-allowlist-guard.test.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ describe("tool allowlist guard", () => {
1919

2020
it("fails closed for runtime toolsAllow when tools are disabled", () => {
2121
const error = buildEmptyExplicitToolAllowlistError({
22-
sources: [{ label: "runtime toolsAllow", entries: ["query_db"] }],
22+
sources: [
23+
{ label: "runtime toolsAllow", entries: ["query_db"], enforceWhenToolsDisabled: true },
24+
],
2325
callableToolNames: [],
2426
toolsEnabled: true,
2527
disableTools: true,
@@ -29,6 +31,17 @@ describe("tool allowlist guard", () => {
2931
expect(error?.message).toContain("tools are disabled for this run");
3032
});
3133

34+
it("allows inherited config allowlists when a run intentionally disables tools", () => {
35+
expect(
36+
buildEmptyExplicitToolAllowlistError({
37+
sources: [{ label: "tools.allow", entries: ["lobster", "llm-task"] }],
38+
callableToolNames: [],
39+
toolsEnabled: true,
40+
disableTools: true,
41+
}),
42+
).toBeNull();
43+
});
44+
3245
it("fails closed when the selected model cannot use requested tools", () => {
3346
const error = buildEmptyExplicitToolAllowlistError({
3447
sources: [{ label: "agents.db.tools.allow", entries: ["query_db"] }],
@@ -63,13 +76,21 @@ describe("tool allowlist guard", () => {
6376
it("keeps source labels for config and runtime allowlists", () => {
6477
const sources = collectExplicitToolAllowlistSources([
6578
{ label: "tools.allow", allow: [" read ", ""] },
66-
{ label: "runtime toolsAllow", allow: ["query_db"] },
79+
{
80+
label: "runtime toolsAllow",
81+
allow: ["query_db"],
82+
enforceWhenToolsDisabled: true,
83+
},
6784
{ label: "tools.byProvider.allow" },
6885
]);
6986

7087
expect(sources).toEqual([
7188
{ label: "tools.allow", entries: ["read"] },
72-
{ label: "runtime toolsAllow", entries: ["query_db"] },
89+
{
90+
label: "runtime toolsAllow",
91+
entries: ["query_db"],
92+
enforceWhenToolsDisabled: true,
93+
},
7394
]);
7495
});
7596
});

src/agents/tool-allowlist-guard.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,24 @@ import { normalizeToolName } from "./tool-policy.js";
33
type ExplicitToolAllowlistSource = {
44
label: string;
55
entries: string[];
6+
enforceWhenToolsDisabled?: boolean;
67
};
78

89
export function collectExplicitToolAllowlistSources(
9-
sources: Array<{ label: string; allow?: string[] }>,
10+
sources: Array<{ label: string; allow?: string[]; enforceWhenToolsDisabled?: boolean }>,
1011
): ExplicitToolAllowlistSource[] {
1112
return sources.flatMap((source) => {
1213
const entries = (source.allow ?? []).map((entry) => entry.trim()).filter(Boolean);
13-
return entries.length ? [{ label: source.label, entries }] : [];
14+
if (entries.length === 0) {
15+
return [];
16+
}
17+
return [
18+
{
19+
label: source.label,
20+
entries,
21+
...(source.enforceWhenToolsDisabled === true ? { enforceWhenToolsDisabled: true } : {}),
22+
},
23+
];
1424
});
1525
}
1626

@@ -20,11 +30,15 @@ export function buildEmptyExplicitToolAllowlistError(params: {
2030
toolsEnabled: boolean;
2131
disableTools?: boolean;
2232
}): Error | null {
33+
const sources =
34+
params.disableTools === true
35+
? params.sources.filter((source) => source.enforceWhenToolsDisabled === true)
36+
: params.sources;
2337
const callableToolNames = params.callableToolNames.map(normalizeToolName).filter(Boolean);
24-
if (params.sources.length === 0 || callableToolNames.length > 0) {
38+
if (sources.length === 0 || callableToolNames.length > 0) {
2539
return null;
2640
}
27-
const requested = params.sources
41+
const requested = sources
2842
.map((source) => `${source.label}: ${source.entries.map(normalizeToolName).join(", ")}`)
2943
.join("; ");
3044
const reason =

0 commit comments

Comments
 (0)