""" lambdagent_guard.crewai — CrewAI guard wrapper (I12). Usage: from lambdagent_guard import guard_crewai crew = Crew(agents=[researcher, writer], tasks=[...]) guarded = guard_crewai(crew, cost_budget=10.0) result = guarded.kickoff() Catches: CrewAI #3847 (max_iter broken), #737 (tool result lost), #1355 (10x cost) """ from __future__ import annotations from typing import Any, Callable, Optional from .core import GuardConfig, RuntimeMonitor, run_compile_checks import warnings def guard_crewai( crew, cost_budget: float = float("inf"), parallel_safety: bool = True, terminate_check: bool = True, loop_detection: bool = True, cost_alert: Optional[Callable[[float], None]] = None, ): """ Wrap a CrewAI Crew with lambdagent safety guards. Args: crew: CrewAI Crew object cost_budget: Max cost in USD parallel_safety: Check agent store independence terminate_check: Verify termination conditions exist """ guard_cfg = GuardConfig( cost_budget=cost_budget, parallel_safety=parallel_safety, terminate_check=terminate_check, loop_detection=loop_detection, cost_alert=cost_alert, ) # Phase 1: Compile-time checks try: from lambdagent.extractors import extract_config config = extract_config(crew, framework="crewai") issues = run_compile_checks(config, guard_cfg) for issue in issues: warnings.warn(f"[lambdagent-guard] {issue}") except Exception as e: warnings.warn(f"[lambdagent-guard] Compile check skipped: {e}") # Phase 2: Runtime hooks — wrap kickoff() monitor = RuntimeMonitor(guard_cfg) original_kickoff = crew.kickoff def guarded_kickoff(*args, **kwargs): # Hook into each agent's execution for agent in getattr(crew, 'agents', []): _hook_crewai_agent(agent, monitor) result = original_kickoff(*args, **kwargs) return result crew.kickoff = guarded_kickoff crew._lambdagent_monitor = monitor return crew def _hook_crewai_agent(agent, monitor: RuntimeMonitor): """Hook into a CrewAI agent's execute_task if available.""" if hasattr(agent, 'execute_task'): original = agent.execute_task def guarded_execute(*args, **kwargs): result = original(*args, **kwargs) monitor.on_step({ "output": str(result)[:500], "tokens": 0, }) return result agent.execute_task = guarded_execute