@@ -7,16 +7,20 @@ use forge_domain::{
77 Agent , AgentId , AgentInput , ChatResponse , ChatResponseContent , ToolCallContext , ToolCallFull ,
88 ToolCatalog , ToolDefinition , ToolName , ToolOutput , ToolResult ,
99} ;
10+ use forge_template:: Element ;
1011use futures:: future:: join_all;
1112use strum:: IntoEnumIterator ;
1213use tokio:: time:: timeout;
1314
1415use crate :: agent_executor:: AgentExecutor ;
1516use crate :: dto:: ToolsOverview ;
1617use crate :: error:: Error ;
18+ use crate :: fmt:: content:: FormatContent ;
1719use crate :: mcp_executor:: McpExecutor ;
1820use crate :: tool_executor:: ToolExecutor ;
19- use crate :: { EnvironmentService , McpService , Services , ToolResolver , WorkspaceService } ;
21+ use crate :: {
22+ EnvironmentService , McpService , PolicyService , Services , ToolResolver , WorkspaceService ,
23+ } ;
2024
2125pub struct ToolRegistry < S > {
2226 tool_executor : ToolExecutor < S > ,
@@ -54,6 +58,36 @@ impl<S: Services> ToolRegistry<S> {
5458 } ) ?
5559 }
5660
61+ /// Check if a tool operation is allowed based on the workflow policies
62+ async fn check_tool_permission (
63+ & self ,
64+ tool_input : & ToolCatalog ,
65+ context : & ToolCallContext ,
66+ ) -> anyhow:: Result < bool > {
67+ let cwd = self . services . get_environment ( ) . cwd ;
68+ let operation = tool_input. to_policy_operation ( cwd. clone ( ) ) ;
69+ if let Some ( operation) = operation {
70+ let decision = self . services . check_operation_permission ( & operation) . await ?;
71+
72+ // Send custom policy message to the user when a policy file was created
73+ if let Some ( policy_path) = decision. path {
74+ use forge_domain:: TitleFormat ;
75+
76+ use crate :: utils:: format_display_path;
77+ context
78+ . send_title (
79+ TitleFormat :: debug ( "Permissions Update" )
80+ . sub_title ( format_display_path ( policy_path. as_path ( ) , & cwd) ) ,
81+ )
82+ . await ?;
83+ }
84+ if !decision. allowed {
85+ return Ok ( true ) ;
86+ }
87+ }
88+ Ok ( false )
89+ }
90+
5791 async fn call_inner (
5892 & self ,
5993 agent : & Agent ,
@@ -67,8 +101,32 @@ impl<S: Services> ToolRegistry<S> {
67101
68102 // First, try to call a Forge tool
69103 if ToolCatalog :: contains ( & input. name ) {
70- self . call_with_timeout ( & tool_name, || self . tool_executor . execute ( input, context) )
71- . await
104+ let tool_input: ToolCatalog = ToolCatalog :: try_from ( input) ?;
105+ let env = self . services . get_environment ( ) ;
106+ if let Some ( content) = tool_input. to_content ( & env) {
107+ context. send ( content) . await ?;
108+ }
109+
110+ // Check permissions before executing the tool (only in restricted mode)
111+ // This is done BEFORE the timeout to ensure permissions are never timed out
112+ if self . services . is_restricted ( )
113+ && self . check_tool_permission ( & tool_input, context) . await ?
114+ {
115+ // Send formatted output message for policy denial
116+ context
117+ . send ( forge_domain:: TitleFormat :: error ( "Permission Denied" ) )
118+ . await ?;
119+
120+ return Ok ( ToolOutput :: text (
121+ Element :: new ( "permission_denied" )
122+ . cdata ( "User has denied the permission to execute this tool" ) ,
123+ ) ) ;
124+ }
125+
126+ self . call_with_timeout ( & tool_name, || {
127+ self . tool_executor . execute ( tool_input, context)
128+ } )
129+ . await
72130 } else if self . agent_executor . contains_tool ( & input. name ) . await ? {
73131 // Handle agent delegation tool calls
74132 let agent_input = AgentInput :: try_from ( & input) ?;
0 commit comments