File size: 1,785 Bytes
da79e97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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)