@@ -30,6 +30,7 @@ import {
3030} from "./subagent-capabilities.js" ;
3131import { isToolAllowedByPolicyName } from "./tool-policy-match.js" ;
3232import {
33+ expandToolGroups ,
3334 mergeAlsoAllowPolicy ,
3435 normalizeToolName ,
3536 resolveToolProfilePolicy ,
@@ -60,6 +61,7 @@ const SUBAGENT_TOOL_DENY_LEAF = [
6061 "sessions_history" ,
6162 "sessions_spawn" ,
6263] ;
64+ const NO_MATCHING_TOOL_ALLOWLIST_ENTRY = "__openclaw_no_matching_subagent_tool_allow__" ;
6365
6466/**
6567 * Build the deny list for a sub-agent at a given depth.
@@ -86,6 +88,39 @@ function resolveSubagentDenyListForRole(role: SubagentSessionRole): string[] {
8688 return [ ...SUBAGENT_TOOL_DENY_ALWAYS ] ;
8789}
8890
91+ function mergeConfiguredSubagentAllow (
92+ allow : string [ ] | undefined ,
93+ alsoAllow : string [ ] | undefined ,
94+ ) : string [ ] | undefined {
95+ return allow && alsoAllow ? Array . from ( new Set ( [ ...allow , ...alsoAllow ] ) ) : allow ;
96+ }
97+
98+ function intersectToolAllowlists (
99+ left : string [ ] | undefined ,
100+ right : string [ ] | undefined ,
101+ ) : string [ ] | undefined {
102+ if ( ! right || right . length === 0 ) {
103+ return left ;
104+ }
105+ if ( ! left || left . length === 0 ) {
106+ return right ;
107+ }
108+ const leftPolicy = { allow : left } ;
109+ const rightPolicy = { allow : right } ;
110+ const result = new Set < string > ( ) ;
111+ for ( const entry of expandToolGroups ( left ) ) {
112+ if ( isToolAllowedByPolicyName ( entry , rightPolicy ) ) {
113+ result . add ( entry ) ;
114+ }
115+ }
116+ for ( const entry of expandToolGroups ( right ) ) {
117+ if ( isToolAllowedByPolicyName ( entry , leftPolicy ) ) {
118+ result . add ( entry ) ;
119+ }
120+ }
121+ return result . size > 0 ? [ ...result ] : [ NO_MATCHING_TOOL_ALLOWLIST_ENTRY ] ;
122+ }
123+
89124export function resolveSubagentToolPolicy ( cfg ?: OpenClawConfig , depth ?: number ) : SandboxToolPolicy {
90125 const configured = cfg ?. tools ?. subagents ?. tools ;
91126 const maxSpawnDepth =
@@ -101,7 +136,7 @@ export function resolveSubagentToolPolicy(cfg?: OpenClawConfig, depth?: number):
101136 ...baseDeny . filter ( ( toolName ) => ! explicitAllow . has ( normalizeToolName ( toolName ) ) ) ,
102137 ...( Array . isArray ( configured ?. deny ) ? configured . deny : [ ] ) ,
103138 ] ;
104- const mergedAllow = allow && alsoAllow ? Array . from ( new Set ( [ ... allow , ... alsoAllow ] ) ) : allow ;
139+ const mergedAllow = mergeConfiguredSubagentAllow ( allow , alsoAllow ) ;
105140 return { allow : mergedAllow , deny } ;
106141}
107142
@@ -124,6 +159,7 @@ export function resolveSubagentToolPolicyForSession(
124159 const inheritedToolPolicy = resolveInheritedToolPolicyForSession ( cfg , sessionKey , {
125160 store,
126161 } ) ;
162+ const inheritedToolAllow = inheritedToolPolicy ?. allow ;
127163 const inheritedToolDeny = inheritedToolPolicy ?. deny ?? [ ] ;
128164 const allow = Array . isArray ( configured ?. allow ) ? configured . allow : undefined ;
129165 const alsoAllow = Array . isArray ( configured ?. alsoAllow ) ? configured . alsoAllow : undefined ;
@@ -137,7 +173,10 @@ export function resolveSubagentToolPolicyForSession(
137173 ...inheritedToolDeny ,
138174 ...( Array . isArray ( configured ?. deny ) ? configured . deny : [ ] ) ,
139175 ] ;
140- const mergedAllow = allow && alsoAllow ? Array . from ( new Set ( [ ...allow , ...alsoAllow ] ) ) : allow ;
176+ const mergedAllow = intersectToolAllowlists (
177+ mergeConfiguredSubagentAllow ( allow , alsoAllow ) ,
178+ inheritedToolAllow ,
179+ ) ;
141180 return { allow : mergedAllow , deny } ;
142181}
143182
0 commit comments