Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,12 @@ async fn command_gateway(port: u16, verbose: bool) -> Result<()> {
}

// Create ToolRegistryExecutor for LLMAgentBuilder
let tool_executor = Arc::new(ToolRegistryExecutor::new(tools.clone()));
let tool_executor = Arc::new(ToolRegistryExecutor::new(
tools.clone(),
"system".to_string(),
None,
None,
));

// Build system prompt
let system_prompt = context.build_system_prompt(None).await.unwrap_or_else(|_| {
Expand Down Expand Up @@ -350,18 +355,18 @@ async fn command_gateway(port: u16, verbose: bool) -> Result<()> {
let channel_manager = ChannelManager::new(&config, bus.clone());

// Initialize RBAC manager if configured (must be before channel registrations)
let rbac_manager: Option<Arc<RbacManager>> = if let Ok(Some(rbac_config)) = config.get_rbac_config() {
if rbac_config.enabled {
let workspace = config.workspace_path();
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("."));
Some(Arc::new(RbacManager::new(rbac_config, workspace, home)))
let rbac_manager: Option<Arc<RbacManager>> =
if let Ok(Some(rbac_config)) = config.get_rbac_config() {
if rbac_config.enabled {
let workspace = config.workspace_path();
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("."));
Some(Arc::new(RbacManager::new(rbac_config, workspace, home)))
} else {
None
}
} else {
None
}
} else {
None
};

};
// register dingtalk channel if enabled
if config.channels.dingtalk.enabled {
let dingtalk = DingTalkChannel::new(config.channels.dingtalk.clone(), bus.clone());
Expand Down Expand Up @@ -513,7 +518,12 @@ async fn command_agent(message: Option<String>, session: String) -> Result<()> {
}

// Create ToolRegistryExecutor for LLMAgentBuilder
let tool_executor = Arc::new(ToolRegistryExecutor::new(tools.clone()));
let tool_executor = Arc::new(ToolRegistryExecutor::new(
tools.clone(),
"system".to_string(),
None,
None,
));

// Build system prompt
let system_prompt = context.build_system_prompt(None).await.unwrap_or_else(|_| {
Expand Down
97 changes: 94 additions & 3 deletions core/src/agent/loop_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ pub struct AgentLoop {
temperature: Option<f32>,
/// Max tokens
max_tokens: Option<u32>,
/// Resource Limiter
resource_limiter: Option<Arc<crate::sandbox::resource::ResourceLimiter>>,
/// RBAC Manager
rbac_manager: Option<Arc<crate::rbac::manager::RbacManager>>,
}

impl AgentLoop {
Expand Down Expand Up @@ -99,6 +103,30 @@ impl AgentLoop {
// Create mofa TaskOrchestrator for subagent spawning
let task_orchestrator = Arc::new(TaskOrchestrator::with_defaults(provider.clone()));

let resource_limiter = config
.sandbox
.as_ref()
.and_then(|s| s.build_limiter())
.map(Arc::new);

let rbac_manager = match config.get_rbac_config() {
Ok(Some(rc)) => {
let home_dir = dirs::home_dir()
.or_else(|| std::env::current_dir().ok())
.unwrap_or_else(|| std::path::PathBuf::from("."));
Some(Arc::new(crate::rbac::manager::RbacManager::new(
rc,
config.workspace_path(),
home_dir,
)))
}
Ok(None) => None,
Err(e) => {
tracing::warn!("RBAC configuration error: {}", e);
None
}
};

Ok(Self {
_agent: agent,
provider,
Expand All @@ -112,6 +140,8 @@ impl AgentLoop {
default_model,
temperature,
max_tokens,
resource_limiter,
rbac_manager,
})
}

Expand All @@ -136,6 +166,30 @@ impl AgentLoop {
// Create mofa TaskOrchestrator for subagent spawning
let task_orchestrator = Arc::new(TaskOrchestrator::with_defaults(provider.clone()));

let resource_limiter = config
.sandbox
.as_ref()
.and_then(|s| s.build_limiter())
.map(Arc::new);

let rbac_manager = match config.get_rbac_config() {
Ok(Some(rc)) => {
let home_dir = dirs::home_dir()
.or_else(|| std::env::current_dir().ok())
.unwrap_or_else(|| std::path::PathBuf::from("."));
Some(Arc::new(crate::rbac::manager::RbacManager::new(
rc,
config.workspace_path(),
home_dir,
)))
}
Ok(None) => None,
Err(e) => {
tracing::warn!("RBAC configuration error: {}", e);
None
}
};

Ok(Self {
_agent: agent,
provider,
Expand All @@ -149,6 +203,8 @@ impl AgentLoop {
default_model,
temperature,
max_tokens,
resource_limiter,
rbac_manager,
})
}

Expand Down Expand Up @@ -269,8 +325,37 @@ impl AgentLoop {
} else {
Some(msg.media.clone())
};
// Determine user
let role = self.rbac_manager.as_ref().map(|manager| {
// Simplified role determination for agent loop: default to guest if not Discord/DingTalk/Feishu
// Real channels will pass correct roles later, but we need *some* role here matching the channel.
// Since AgentLoop only has channel/user_id, it can't check Discord roles directly.
// So we rely on mapping default rules or overrides.
let roles_val = msg.metadata.get("roles");
let roles: Vec<String> = if let Some(serde_json::Value::Array(arr)) = roles_val {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
} else {
Vec::new()
};

match response_channel.as_str() {
"discord" => manager.get_role_from_discord(&msg.sender_id, &roles),
"dingtalk" => manager.get_role_from_dingtalk(&msg.sender_id, &roles),
"feishu" => manager.get_role_from_feishu(&msg.sender_id, &roles),
_ => crate::rbac::Role::Guest,
}
});

let final_content = self
.run_agent_loop(context_messages, &msg.content, media)
.run_agent_loop(
context_messages,
&msg.content,
media,
msg.sender_id.clone(),
role,
)
.await?;

// Save to session
Expand Down Expand Up @@ -306,9 +391,15 @@ impl AgentLoop {
context: Vec<ChatMessage>,
content: &str,
media: Option<Vec<String>>,
user_id: String,
role: Option<crate::rbac::Role>,
) -> Result<Option<String>> {
let tool_executor = Arc::new(ToolRegistryExecutor::new(self.tools.clone()))
as Arc<dyn mofa_sdk::llm::ToolExecutor>;
let tool_executor = Arc::new(ToolRegistryExecutor::new(
self.tools.clone(),
user_id,
self.resource_limiter.clone(),
role,
)) as Arc<dyn mofa_sdk::llm::ToolExecutor>;

let config = MofaAgentLoopConfig {
max_tool_iterations: self.max_iterations,
Expand Down
3 changes: 2 additions & 1 deletion core/src/channels/feishu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,8 @@ impl Channel for FeishuChannel {

// Attach RBAC role to metadata when available
if let Some(ref rbac_mgr) = rbac {
let role: Role = rbac_mgr.get_role_from_feishu(sender, &[]);
let empty_tags: Vec<String> = Vec::new();
let role: Role = rbac_mgr.get_role_from_feishu(sender, &empty_tags);
metadata.insert("role".to_string(), Value::String(role.as_str().to_string()));
}

Expand Down
4 changes: 4 additions & 0 deletions core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use crate::error::{ConfigError, Result};
use crate::rbac::config::RbacConfig;
use crate::sandbox::config::SandboxConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tokio::fs;
Expand Down Expand Up @@ -376,6 +377,9 @@ pub struct Config {
/// RBAC configuration
#[serde(default)]
pub rbac: Option<RbacConfig>,
/// Sandbox configuration
#[serde(default)]
pub sandbox: Option<SandboxConfig>,
}

impl Config {
Expand Down
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod permissions;
pub mod provider;
pub mod python_env;
pub mod rbac;
pub mod sandbox;
pub mod session;
pub mod tools;
pub mod types;
Expand Down
Loading
Loading