From 8d144ac1399ada9d0b2476d4ca6af19ca84f6a47 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Mon, 18 May 2026 20:42:57 +0800 Subject: [PATCH] feat(agent): add architect, debugger, implementer, tester, security sub-agent roles Extend delegation system with 5 new specialized roles alongside researcher/analyst/reviewer. Each role has curated tool access. Refactor profile lookup to use profile_for_role_name and update compact/summarizer and tool context accordingly. --- libs/agent/chat/agent_profile.rs | 229 ++++++++++++++++++++++++++- libs/agent/chat/chat_execution.rs | 12 +- libs/agent/chat/mod.rs | 5 + libs/agent/chat/orchestrator.rs | 43 +++-- libs/agent/chat/service.rs | 10 +- libs/agent/compact/room_compactor.rs | 4 +- libs/agent/compact/summarizer.rs | 28 ++-- libs/agent/lib.rs | 4 +- libs/agent/tool/context.rs | 7 +- 9 files changed, 276 insertions(+), 66 deletions(-) diff --git a/libs/agent/chat/agent_profile.rs b/libs/agent/chat/agent_profile.rs index 1afbd46..da10dd7 100644 --- a/libs/agent/chat/agent_profile.rs +++ b/libs/agent/chat/agent_profile.rs @@ -15,6 +15,7 @@ fn shared_tools() -> Vec { "git_file_content".into(), "git_blob_get".into(), "git_blob_content".into(), + "git_status".into(), ] } @@ -25,8 +26,10 @@ fn researcher_tools() -> Vec { "git_search_commits".into(), "repo_search".into(), "repo_doc_search".into(), + "project_bing_search".into(), "project_arxiv_search".into(), "project_curl".into(), + "curl_exec".into(), // Index / overview "repo_overview".into(), "repo_readme".into(), @@ -45,6 +48,10 @@ fn researcher_tools() -> Vec { "git_graph".into(), "git_commit_info".into(), "repo_commit_log".into(), + "git_ref_list".into(), + "git_ref_info".into(), + "git_lfs_summary".into(), + "git_lfs_scan_tree".into(), ] } @@ -65,6 +72,9 @@ fn analyst_tools() -> Vec { "git_branch_list".into(), "git_branch_info".into(), "git_branch_diff".into(), + "git_merge_analysis".into(), + "git_ref_list".into(), + "git_ref_info".into(), // Project data "project_list_issues".into(), "project_list_repos".into(), @@ -81,6 +91,7 @@ fn reviewer_tools() -> Vec { // Merge status "git_branches_merged".into(), "git_branch_info".into(), + "git_merge_analysis".into(), // Tracking "project_list_issues".into(), "project_update_issue".into(), @@ -90,6 +101,108 @@ fn reviewer_tools() -> Vec { ] } +/// Architect-specific tools (system design & dependency mapping). +fn architect_tools() -> Vec { + vec![ + // Repository structure + "repo_overview".into(), + "repo_readme".into(), + "repo_file_tree".into(), + "repo_languages".into(), + "repo_dependencies".into(), + "repo_test_discovery".into(), + // Change and branch context + "repo_diff_summary".into(), + "git_branch_list".into(), + "git_branch_info".into(), + "git_branch_diff".into(), + "git_merge_analysis".into(), + "git_ref_list".into(), + // Project context + "project_list_repos".into(), + "project_list_issues".into(), + ] +} + +/// Debugger-specific tools (root-cause analysis & history tracing). +fn debugger_tools() -> Vec { + vec![ + // Failure and change inspection + "git_show".into(), + "git_diff".into(), + "git_diff_stats".into(), + "git_blame".into(), + "git_log".into(), + "git_commit_info".into(), + "git_ref_info".into(), + // Structural clues + "repo_file_tree".into(), + "repo_dependencies".into(), + "repo_test_discovery".into(), + "repo_doc_search".into(), + // Issue context + "project_list_issues".into(), + ] +} + +/// Implementer-specific tools (implementation planning & code navigation). +fn implementer_tools() -> Vec { + vec![ + // Code and documentation context + "repo_overview".into(), + "repo_readme".into(), + "repo_file_tree".into(), + "repo_doc_index".into(), + "repo_doc_read".into(), + "repo_dependencies".into(), + "repo_test_discovery".into(), + // Current change context + "git_diff".into(), + "git_diff_stats".into(), + "git_show".into(), + "git_branch_info".into(), + // Project context + "project_list_issues".into(), + ] +} + +/// Tester-specific tools (coverage, regression, and validation planning). +fn tester_tools() -> Vec { + vec![ + // Test discovery and changed surface + "git_grep".into(), + "git_diff".into(), + "git_diff_stats".into(), + "git_show".into(), + "repo_file_tree".into(), + "repo_dependencies".into(), + "repo_test_discovery".into(), + // Project context + "project_list_issues".into(), + ] +} + +/// Security-specific tools (threat modeling & sensitive-code review). +fn security_tools() -> Vec { + vec![ + // Sensitive pattern discovery + "git_grep".into(), + "git_diff".into(), + "git_diff_stats".into(), + "git_blame".into(), + "git_log".into(), + // Dependency and surface review + "repo_file_tree".into(), + "repo_dependencies".into(), + "repo_doc_search".into(), + "git_lfs_summary".into(), + "git_lfs_pointer_info".into(), + "git_lfs_object_info".into(), + // Issue context + "project_list_issues".into(), + ] +} + /// Supervisor-specific tools (delegation & synthesis). fn supervisor_tools() -> Vec { vec![ @@ -105,12 +218,31 @@ pub fn tools_for_role(role: &AgentRole) -> Vec { AgentRole::Researcher => tools.extend(researcher_tools()), AgentRole::Analyst => tools.extend(analyst_tools()), AgentRole::Reviewer => tools.extend(reviewer_tools()), + AgentRole::Architect => tools.extend(architect_tools()), + AgentRole::Debugger => tools.extend(debugger_tools()), + AgentRole::Implementer => tools.extend(implementer_tools()), + AgentRole::Tester => tools.extend(tester_tools()), + AgentRole::Security => tools.extend(security_tools()), AgentRole::Supervisor => tools.extend(supervisor_tools()), AgentRole::Default => {} // Default role gets only shared tools } tools } +pub fn profile_for_role_name(role: &str) -> AgentExecutionProfile { + match role { + "researcher" => researcher_profile(), + "analyst" => analyst_profile(), + "reviewer" => reviewer_profile(), + "architect" => architect_profile(), + "debugger" => debugger_profile(), + "implementer" => implementer_profile(), + "tester" => tester_profile(), + "security" => security_profile(), + _ => researcher_profile(), + } +} + pub fn supervisor_profile() -> AgentExecutionProfile { AgentExecutionProfile { role: AgentRole::Supervisor, @@ -123,6 +255,11 @@ pub fn supervisor_profile() -> AgentExecutionProfile { - **researcher**: Gathers concrete facts, evidence, and data from tools and context. Best for finding information, searching code, and discovering evidence.\n\ - **analyst**: Builds coherent explanations, highlights causal links, edge cases, and tradeoffs. Best for explaining findings and reasoning about implications.\n\ - **reviewer**: Stress-tests proposals, identifies contradictions, missing assumptions, regressions, and risks. Best for quality checks and risk assessment.\n\ + - **architect**: Maps systems, boundaries, dependencies, and long-term design tradeoffs. Best for architecture decisions and refactor strategy.\n\ + - **debugger**: Finds likely root causes, reproduction gaps, and suspect changes. Best for failures, regressions, and confusing behavior.\n\ + - **implementer**: Turns requirements into concrete implementation steps, affected files, and integration concerns. Best for execution planning.\n\ + - **tester**: Designs validation strategy, regression coverage, and edge-case test plans. Best for test planning and release confidence.\n\ + - **security**: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for threat modeling and sensitive changes.\n\ - Provide a clear, focused task description for each sub-agent.\n\ - You may call multiple sub-agents in sequence (call one, review its output, then decide to call another).\n\ - You may also call the same role twice with different tasks if needed.\n\ @@ -130,7 +267,10 @@ pub fn supervisor_profile() -> AgentExecutionProfile { ## Decision Guide\n\ - Simple factual questions: call researcher only.\n\ - Questions requiring explanation: call researcher then analyst.\n\ - - Design/architecture reviews: call researcher, analyst, then reviewer.\n\ + - Design/architecture reviews: call researcher, architect, then reviewer.\n\ + - Bug or regression diagnosis: call debugger, then tester if validation is needed.\n\ + - Implementation requests: call implementer, then reviewer for risk checks.\n\ + - Security-sensitive changes: call security, then reviewer.\n\ - If a sub-agent's output is insufficient, call another sub-agent for clarification.\n\ \n\ ## Output Rules\n\ @@ -201,8 +341,93 @@ pub fn reviewer_profile() -> AgentExecutionProfile { } } +pub fn architect_profile() -> AgentExecutionProfile { + AgentExecutionProfile { + role: AgentRole::Architect, + system_prompt: Some( + "You are the architect agent. Map system boundaries, dependencies, data flow, and design tradeoffs. Prefer practical architecture guidance tied to repository evidence. Call out migration risks and long-term maintainability concerns.".to_string(), + ), + temperature: Some(0.2), + max_tokens: Some(2000), + top_p: Some(1.0), + frequency_penalty: Some(0.0), + presence_penalty: Some(0.0), + max_tool_depth: Some(5), + allowed_tools: Some(tools_for_role(&AgentRole::Architect)), + disable_orchestration: true, + } +} + +pub fn debugger_profile() -> AgentExecutionProfile { + AgentExecutionProfile { + role: AgentRole::Debugger, + system_prompt: Some( + "You are the debugger agent. Identify likely root causes, suspect files or commits, missing reproduction details, and the shortest validation path. Separate evidence from hypotheses and rank hypotheses by plausibility.".to_string(), + ), + temperature: Some(0.1), + max_tokens: Some(1800), + top_p: Some(1.0), + frequency_penalty: Some(0.0), + presence_penalty: Some(0.0), + max_tool_depth: Some(6), + allowed_tools: Some(tools_for_role(&AgentRole::Debugger)), + disable_orchestration: true, + } +} + +pub fn implementer_profile() -> AgentExecutionProfile { + AgentExecutionProfile { + role: AgentRole::Implementer, + system_prompt: Some( + "You are the implementer agent. Convert requirements into a concrete execution plan: files to touch, sequencing, integration points, and risks. Keep recommendations actionable and avoid broad rewrites unless justified.".to_string(), + ), + temperature: Some(0.15), + max_tokens: Some(1800), + top_p: Some(1.0), + frequency_penalty: Some(0.0), + presence_penalty: Some(0.0), + max_tool_depth: Some(5), + allowed_tools: Some(tools_for_role(&AgentRole::Implementer)), + disable_orchestration: true, + } +} + +pub fn tester_profile() -> AgentExecutionProfile { + AgentExecutionProfile { + role: AgentRole::Tester, + system_prompt: Some( + "You are the tester agent. Design high-signal validation: unit, integration, regression, and edge-case coverage. Identify what must be tested, what can be skipped, and the fastest commands or checks to build confidence.".to_string(), + ), + temperature: Some(0.1), + max_tokens: Some(1600), + top_p: Some(1.0), + frequency_penalty: Some(0.0), + presence_penalty: Some(0.0), + max_tool_depth: Some(4), + allowed_tools: Some(tools_for_role(&AgentRole::Tester)), + disable_orchestration: true, + } +} + +pub fn security_profile() -> AgentExecutionProfile { + AgentExecutionProfile { + role: AgentRole::Security, + system_prompt: Some( + "You are the security agent. Review authentication, authorization, data exposure, injection, dependency, secret-handling, and abuse-case risks. Prioritize exploitable issues and concrete mitigations over generic advice.".to_string(), + ), + temperature: Some(0.1), + max_tokens: Some(1800), + top_p: Some(1.0), + frequency_penalty: Some(0.0), + presence_penalty: Some(0.0), + max_tool_depth: Some(5), + allowed_tools: Some(tools_for_role(&AgentRole::Security)), + disable_orchestration: true, + } +} + /// Whether to enable multi-agent delegation for this request. /// Simplified from keyword-based gating: delegation is enabled when tools are available. pub fn should_enable_delegation(_input: &str, tools_available: bool) -> bool { tools_available -} \ No newline at end of file +} diff --git a/libs/agent/chat/chat_execution.rs b/libs/agent/chat/chat_execution.rs index cab5885..3cf6d61 100644 --- a/libs/agent/chat/chat_execution.rs +++ b/libs/agent/chat/chat_execution.rs @@ -2,7 +2,7 @@ use std::pin::Pin; use std::sync::Arc; use uuid::Uuid; -use super::agent_profile::{analyst_profile, researcher_profile, reviewer_profile}; +use super::agent_profile::profile_for_role_name; use crate::client::AiClientConfig; use crate::client::types::{ChatRequestMessage, ToolCall}; use crate::client::{StreamChunk, StreamChunkType, StreamedToolCall, call_stream}; @@ -629,13 +629,13 @@ pub async fn execute_chat_stream( "type": "function", "function": { "name": "call_sub_agent", - "description": "Delegate a task to a specialist sub-agent and receive its output.\nAvailable roles:\n- researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n- analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n- reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\nProvide a clear, focused task description so the sub-agent knows exactly what to investigate.", + "description": "Delegate a task to a specialist sub-agent and receive its output.\nAvailable roles:\n- researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n- analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n- reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\n- architect: Maps systems, dependencies, boundaries, and design tradeoffs. Best for architecture decisions.\n- debugger: Finds root causes, suspect changes, and validation paths. Best for bugs and regressions.\n- implementer: Converts requirements into concrete implementation steps. Best for execution planning.\n- tester: Designs validation and regression coverage. Best for test strategy.\n- security: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for sensitive changes.\nProvide a clear, focused task description so the sub-agent knows exactly what to investigate.", "parameters": { "type": "object", "properties": { "role": { "type": "string", - "description": "The sub-agent role to delegate to: researcher, analyst, or reviewer." + "description": "The sub-agent role to delegate to: researcher, analyst, reviewer, architect, debugger, implementer, tester, or security." }, "task": { "type": "string", @@ -805,11 +805,7 @@ pub async fn execute_chat_stream( .unwrap_or("researcher"); let task = args.get("task").and_then(|v| v.as_str()).unwrap_or(""); - let profile = match role { - "analyst" => analyst_profile(), - "reviewer" => reviewer_profile(), - _ => researcher_profile(), - }; + let profile = profile_for_role_name(role); // Generate children_id BEFORE starting sub-agent execution let sub_agent_id = format!("sub-agent-{}", Uuid::now_v7()); diff --git a/libs/agent/chat/mod.rs b/libs/agent/chat/mod.rs index 8ff0f72..ae08abd 100644 --- a/libs/agent/chat/mod.rs +++ b/libs/agent/chat/mod.rs @@ -87,6 +87,11 @@ pub enum AgentRole { Researcher, Analyst, Reviewer, + Architect, + Debugger, + Implementer, + Tester, + Security, } #[derive(Debug, Clone, Default)] diff --git a/libs/agent/chat/orchestrator.rs b/libs/agent/chat/orchestrator.rs index 5adc7af..8cf243f 100644 --- a/libs/agent/chat/orchestrator.rs +++ b/libs/agent/chat/orchestrator.rs @@ -1,9 +1,6 @@ use std::collections::HashMap; -use super::agent_profile::{ - analyst_profile, researcher_profile, reviewer_profile, should_enable_delegation, - supervisor_profile, -}; +use super::agent_profile::{profile_for_role_name, should_enable_delegation, supervisor_profile}; use super::message_builder::MessageBuilder; use super::nonstreaming_execution::execute_process; use super::service::{ProcessResult, StreamResult}; @@ -67,9 +64,8 @@ pub async fn execute_orchestrated_process( supervisor_request.frequency_penalty = profile .frequency_penalty .unwrap_or(request.frequency_penalty); - supervisor_request.presence_penalty = profile - .presence_penalty - .unwrap_or(request.presence_penalty); + supervisor_request.presence_penalty = + profile.presence_penalty.unwrap_or(request.presence_penalty); execute_process( supervisor_request, @@ -138,9 +134,8 @@ pub async fn execute_orchestrated_stream( supervisor_request.frequency_penalty = profile .frequency_penalty .unwrap_or(request.frequency_penalty); - supervisor_request.presence_penalty = profile - .presence_penalty - .unwrap_or(request.presence_penalty); + supervisor_request.presence_penalty = + profile.presence_penalty.unwrap_or(request.presence_penalty); super::streaming_execution::execute_process_stream( supervisor_request, @@ -175,6 +170,11 @@ fn register_call_sub_agent_tool( - researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n\ - analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n\ - reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\n\ + - architect: Maps systems, dependencies, boundaries, and design tradeoffs. Best for architecture decisions.\n\ + - debugger: Finds root causes, suspect changes, and validation paths. Best for bugs and regressions.\n\ + - implementer: Converts requirements into concrete implementation steps. Best for execution planning.\n\ + - tester: Designs validation and regression coverage. Best for test strategy.\n\ + - security: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for sensitive changes.\n\ Provide a clear, focused task description so the sub-agent knows exactly what to investigate.", ) .parameters(ToolSchema { @@ -187,7 +187,7 @@ fn register_call_sub_agent_tool( name: "role".into(), param_type: "string".into(), description: Some( - "The sub-agent role to delegate to: researcher, analyst, or reviewer.".into(), + "The sub-agent role to delegate to: researcher, analyst, reviewer, architect, debugger, implementer, tester, or security.".into(), ), required: true, properties: None, @@ -224,12 +224,7 @@ fn register_call_sub_agent_tool( .unwrap_or("") .to_owned(); - let profile = match role.as_str() { - "researcher" => researcher_profile(), - "analyst" => analyst_profile(), - "reviewer" => reviewer_profile(), - _ => researcher_profile(), - }; + let profile = profile_for_role_name(role.as_str()); let mut sub_request = captured_request.clone(); sub_request.input = format!( @@ -288,9 +283,12 @@ fn filter_tools_for_sub_agent( let Some(tools) = original_tools else { return Vec::new(); }; - let allowed = allowed_tools - .as_ref() - .map(|list| list.iter().filter(|n| *n != "call_sub_agent").cloned().collect::>()); + let allowed = allowed_tools.as_ref().map(|list| { + list.iter() + .filter(|n| *n != "call_sub_agent") + .cloned() + .collect::>() + }); match allowed { Some(allowed_list) if !allowed_list.is_empty() => tools @@ -308,8 +306,7 @@ fn filter_tools_for_sub_agent( _ => tools .iter() .filter(|tool| { - tool - .get("function") + tool.get("function") .and_then(|f| f.get("name")) .and_then(|v| v.as_str()) .is_some_and(|name| name != "call_sub_agent") @@ -317,4 +314,4 @@ fn filter_tools_for_sub_agent( .cloned() .collect(), } -} \ No newline at end of file +} diff --git a/libs/agent/chat/service.rs b/libs/agent/chat/service.rs index bf79718..99b9bf7 100644 --- a/libs/agent/chat/service.rs +++ b/libs/agent/chat/service.rs @@ -165,10 +165,7 @@ impl ChatService { request: AiRequest, room_tools: ToolRegistry, ) -> Result { - let mut merged = self - .tool_registry - .clone() - .unwrap_or_default(); + let mut merged = self.tool_registry.clone().unwrap_or_default(); merged.merge(room_tools); super::nonstreaming_execution::execute_process( @@ -191,10 +188,7 @@ impl ChatService { on_chunk: StreamCallback, room_tools: ToolRegistry, ) -> Result { - let mut merged = self - .tool_registry - .clone() - .unwrap_or_default(); + let mut merged = self.tool_registry.clone().unwrap_or_default(); merged.merge(room_tools); super::streaming_execution::execute_process_stream( diff --git a/libs/agent/compact/room_compactor.rs b/libs/agent/compact/room_compactor.rs index ed0bc0c..b7470a8 100644 --- a/libs/agent/compact/room_compactor.rs +++ b/libs/agent/compact/room_compactor.rs @@ -198,9 +198,7 @@ impl super::CompactService { .unwrap_or_else(|_| estimate_input.len() / 4); let retain_count = Self::resolve_retain_count(config, estimated_tokens); - if estimated_tokens >= config.token_threshold - && messages.len() > retain_count - { + if estimated_tokens >= config.token_threshold && messages.len() > retain_count { let split_index = messages.len().saturating_sub(retain_count); let (to_summarize, retained_messages) = messages.split_at(split_index); let from_seq = to_summarize diff --git a/libs/agent/compact/summarizer.rs b/libs/agent/compact/summarizer.rs index 0b3bf3c..686866c 100644 --- a/libs/agent/compact/summarizer.rs +++ b/libs/agent/compact/summarizer.rs @@ -2,11 +2,11 @@ use models::rooms::room_message::Model as RoomMessageModel; use models::users::user::{Column as UserCol, Entity as User}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use crate::AgentError; use crate::client::call_with_params; use crate::client::types::ChatRequestMessage; use crate::compact::types::{CompactConfig, MessageSummary}; use crate::tokent::{TokenUsage, count_message_text}; -use crate::AgentError; const DEFAULT_MODEL_CONTEXT_LIMIT: usize = 128_000; const MODEL_INPUT_RATIO_NUMERATOR: usize = 85; @@ -193,7 +193,10 @@ impl super::CompactService { ) .await?; - Ok((final_summary, if has_usage { Some(total_usage) } else { None })) + Ok(( + final_summary, + if has_usage { Some(total_usage) } else { None }, + )) } async fn merge_summary_rounds( @@ -230,11 +233,7 @@ impl super::CompactService { for pair_text in fitted_pairs { let prompt = self.build_prompt(kind, true, &pair_text, current_budget); let (summary, usage) = self - .invoke_summary_prompt( - &prompt, - current_budget, - Self::temperature_for(kind), - ) + .invoke_summary_prompt(&prompt, current_budget, Self::temperature_for(kind)) .await?; Self::accumulate_usage(total_usage, has_usage, usage); next_round.push(summary); @@ -474,18 +473,16 @@ impl super::CompactService { } fn safe_model_input_budget_from_limit(model_context_limit: Option) -> usize { - let context_limit = model_context_limit.unwrap_or(DEFAULT_MODEL_CONTEXT_LIMIT).max(1); + let context_limit = model_context_limit + .unwrap_or(DEFAULT_MODEL_CONTEXT_LIMIT) + .max(1); context_limit .saturating_mul(MODEL_INPUT_RATIO_NUMERATOR) .saturating_div(MODEL_INPUT_RATIO_DENOMINATOR) .max(1) } - fn accumulate_usage( - total: &mut TokenUsage, - has_usage: &mut bool, - usage: Option, - ) { + fn accumulate_usage(total: &mut TokenUsage, has_usage: &mut bool, usage: Option) { if let Some(usage) = usage { total.input_tokens += usage.input_tokens; total.output_tokens += usage.output_tokens; @@ -500,7 +497,10 @@ mod tests { #[test] fn room_summary_uses_eighty_five_percent_input_budget() { - assert_eq!(CompactService::safe_model_input_budget_from_limit(Some(1000)), 850); + assert_eq!( + CompactService::safe_model_input_budget_from_limit(Some(1000)), + 850 + ); } #[test] diff --git a/libs/agent/lib.rs b/libs/agent/lib.rs index 0a85f64..b228fdd 100644 --- a/libs/agent/lib.rs +++ b/libs/agent/lib.rs @@ -19,8 +19,8 @@ pub use billing::{ initialize_user_billing, persist_billing_error, record_ai_usage, record_user_ai_usage, }; pub use chat::{ - AgentExecutionProfile, AgentRole, AiContextSenderType, AiRequest, AiStreamChunk, - ChatService, Mention, RoomMessageContext, StreamCallback, + AgentExecutionProfile, AgentRole, AiContextSenderType, AiRequest, AiStreamChunk, ChatService, + Mention, RoomMessageContext, StreamCallback, }; pub use client::types::ChatRequestMessage; pub use client::{AiCallResponse, AiClientConfig, call_with_params, call_with_retry}; diff --git a/libs/agent/tool/context.rs b/libs/agent/tool/context.rs index ff402d2..4966362 100644 --- a/libs/agent/tool/context.rs +++ b/libs/agent/tool/context.rs @@ -177,12 +177,7 @@ impl ToolContext { return Err(existing); } - match current.compare_exchange( - existing, - next, - Ordering::AcqRel, - Ordering::Relaxed, - ) { + match current.compare_exchange(existing, next, Ordering::AcqRel, Ordering::Relaxed) { Ok(_) => return Ok(()), Err(_) => continue, }