Agents/Subagents: honor subagent alsoAllow grants
This commit is contained in:
@@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/Transcripts: validate assistant tool-call names (syntax/length + registered tool allowlist) before transcript persistence and during replay sanitization so malformed failover tool names no longer poison sessions with repeated provider HTTP 400 errors. (#23324) Thanks @johnsantry.
|
||||
- Agents/Compaction: strip stale assistant usage snapshots from pre-compaction turns when replaying history after a compaction summary so context-token estimation no longer reuses pre-compaction totals and immediately re-triggers destructive follow-up compactions. (#19127) Thanks @tedwatson.
|
||||
- Agents/Replies: emit a default completion acknowledgement (`✅ Done.`) when runs execute tools successfully but return no final assistant text, preventing silent no-reply turns after tool-only completions. (#22834) Thanks @Oldshue.
|
||||
- Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero.
|
||||
- Agents/Diagnostics: include resolved lifecycle error text in `embedded run agent end` warnings so UI/TUI “Connection error” runs expose actionable provider failure reasons in gateway logs. (#23054) Thanks @Raize.
|
||||
- Plugins/Hooks: run legacy `before_agent_start` once per agent turn and reuse that result across model-resolve and prompt-build compatibility paths, preventing duplicate hook side effects (for example duplicate external API calls). (#23289) Thanks @ksato8710.
|
||||
- Models/Config: default missing Anthropic provider/model `api` fields to `anthropic-messages` during config validation so custom relay model entries are preserved instead of being dropped by runtime model registry validation. (#23332) Thanks @bigbigmonkey123.
|
||||
|
||||
@@ -54,6 +54,63 @@ describe("resolveSubagentToolPolicy depth awareness", () => {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 1 } } },
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
it("applies subagent tools.alsoAllow to re-enable default-denied tools", () => {
|
||||
const cfg = {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 2 } } },
|
||||
tools: { subagents: { tools: { alsoAllow: ["sessions_send"] } } },
|
||||
} as unknown as OpenClawConfig;
|
||||
const policy = resolveSubagentToolPolicy(cfg, 1);
|
||||
expect(isToolAllowedByPolicyName("sessions_send", policy)).toBe(true);
|
||||
expect(isToolAllowedByPolicyName("cron", policy)).toBe(false);
|
||||
});
|
||||
|
||||
it("applies subagent tools.allow to re-enable default-denied tools", () => {
|
||||
const cfg = {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 2 } } },
|
||||
tools: { subagents: { tools: { allow: ["sessions_send"] } } },
|
||||
} as unknown as OpenClawConfig;
|
||||
const policy = resolveSubagentToolPolicy(cfg, 1);
|
||||
expect(isToolAllowedByPolicyName("sessions_send", policy)).toBe(true);
|
||||
});
|
||||
|
||||
it("merges subagent tools.alsoAllow into tools.allow when both are set", () => {
|
||||
const cfg = {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 2 } } },
|
||||
tools: {
|
||||
subagents: { tools: { allow: ["sessions_spawn"], alsoAllow: ["sessions_send"] } },
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const policy = resolveSubagentToolPolicy(cfg, 1);
|
||||
expect(policy.allow).toEqual(["sessions_spawn", "sessions_send"]);
|
||||
});
|
||||
|
||||
it("keeps configured deny precedence over allow and alsoAllow", () => {
|
||||
const cfg = {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 2 } } },
|
||||
tools: {
|
||||
subagents: {
|
||||
tools: {
|
||||
allow: ["sessions_send"],
|
||||
alsoAllow: ["sessions_send"],
|
||||
deny: ["sessions_send"],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const policy = resolveSubagentToolPolicy(cfg, 1);
|
||||
expect(isToolAllowedByPolicyName("sessions_send", policy)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not create a restrictive allowlist when only alsoAllow is configured", () => {
|
||||
const cfg = {
|
||||
agents: { defaults: { subagents: { maxSpawnDepth: 2 } } },
|
||||
tools: { subagents: { tools: { alsoAllow: ["sessions_send"] } } },
|
||||
} as unknown as OpenClawConfig;
|
||||
const policy = resolveSubagentToolPolicy(cfg, 1);
|
||||
expect(policy.allow).toBeUndefined();
|
||||
expect(isToolAllowedByPolicyName("subagents", policy)).toBe(true);
|
||||
});
|
||||
|
||||
it("depth-1 orchestrator (maxSpawnDepth=2) allows sessions_spawn", () => {
|
||||
const policy = resolveSubagentToolPolicy(baseCfg, 1);
|
||||
expect(isToolAllowedByPolicyName("sessions_spawn", policy)).toBe(true);
|
||||
|
||||
@@ -88,9 +88,17 @@ export function resolveSubagentToolPolicy(cfg?: OpenClawConfig, depth?: number):
|
||||
cfg?.agents?.defaults?.subagents?.maxSpawnDepth ?? DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH;
|
||||
const effectiveDepth = typeof depth === "number" && depth >= 0 ? depth : 1;
|
||||
const baseDeny = resolveSubagentDenyList(effectiveDepth, maxSpawnDepth);
|
||||
const deny = [...baseDeny, ...(Array.isArray(configured?.deny) ? configured.deny : [])];
|
||||
const allow = Array.isArray(configured?.allow) ? configured.allow : undefined;
|
||||
return { allow, deny };
|
||||
const alsoAllow = Array.isArray(configured?.alsoAllow) ? configured.alsoAllow : undefined;
|
||||
const explicitAllow = new Set(
|
||||
[...(allow ?? []), ...(alsoAllow ?? [])].map((toolName) => normalizeToolName(toolName)),
|
||||
);
|
||||
const deny = [
|
||||
...baseDeny.filter((toolName) => !explicitAllow.has(normalizeToolName(toolName))),
|
||||
...(Array.isArray(configured?.deny) ? configured.deny : []),
|
||||
];
|
||||
const mergedAllow = allow && alsoAllow ? Array.from(new Set([...allow, ...alsoAllow])) : allow;
|
||||
return { allow: mergedAllow, deny };
|
||||
}
|
||||
|
||||
export function isToolAllowedByPolicyName(name: string, policy?: SandboxToolPolicy): boolean {
|
||||
|
||||
@@ -520,6 +520,8 @@ export type ToolsConfig = {
|
||||
model?: string | { primary?: string; fallbacks?: string[] };
|
||||
tools?: {
|
||||
allow?: string[];
|
||||
/** Additional allowlist entries merged into allow and/or default sub-agent denylist. */
|
||||
alsoAllow?: string[];
|
||||
deny?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user