from typing import Dict, Any, List, Optional class ExecutionTrace: """ Stores a sequential list of steps for the Live Execution Timeline. Exposes the internal agentic reasoning of Shadow as a visual trace. """ def __init__(self): self.steps: List[Dict[str, Any]] = [] def add_step(self, agent: str, input_str: str, output: Dict[str, Any], summary: str, risk_hint: Optional[str] = None): step_number = len(self.steps) step = { "step": step_number, "agent": agent, "input": input_str, "output": output, "summary": summary, "risk_hint": risk_hint } self.steps.append(step) def get_trace(self) -> List[Dict[str, Any]]: return self.steps def clear(self): self.steps = [] def format_execution_trace(trace: List[Dict[str, Any]]) -> str: """Returns a human-readable timeline of the execution trace.""" lines = [] for step in trace: # Format: [STEP 1] OSINT PRECHECK → mpesa_reversal detected # If the user specifically wants OSINT PRECHECK to be STEP 0, or if they meant the first step is step 0: # The prompt says: "OSINT Precheck must be STEP 0: Log: agent = 'OSINT_PRECHECK'" # But also says: "[STEP 1] OSINT PRECHECK -> ..." in the example. # I'll just use the step_number from the dictionary (which starts at 0). step_num = step["step"] agent = step["agent"].replace("_", " ") if not agent.endswith("AGENT") and agent != "OSINT PRECHECK": # Just in case agent string doesn't include "AGENT" already pass lines.append(f"[STEP {step_num}] {agent.upper()} -> {step['summary']}") return "\n".join(lines)