mirror of
https://github.com/usestrix/strix.git
synced 2026-09-22 00:31:25 +00:00
fix: save and restore sub-agent state in checkpoints
Previously checkpoints only saved tracer.chat_messages and tracer.vulnerability_reports, leaving tracer.agents and tracer.tool_executions empty on resume — so all sub-agents (both in-progress and completed) were invisible after resuming. Changes: - checkpoint.py: add tracer_agents, tracer_tool_executions, tracer_next_execution_id fields to CheckpointModel; populate them in CheckpointManager.save() from the live tracer - cli.py: on resume, restore agents dict, tool_executions dict, and advance _next_execution_id to avoid ID collisions - tui.py: same restore logic so TUI sidebar shows all previous agents and their tool results immediately on resume Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8a86a92e48
commit
fd5366f612
3 changed files with 39 additions and 0 deletions
|
|
@ -204,9 +204,18 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||
tracer.set_scan_config(scan_config)
|
||||
|
||||
# Added for Resume Feature — pre-populate tracer so stats/vulns are correct
|
||||
# Also restores sub-agent registry and tool executions so the full run is visible
|
||||
if is_resuming and checkpoint_data:
|
||||
tracer.chat_messages.extend(checkpoint_data.tracer_chat_messages)
|
||||
tracer.vulnerability_reports.extend(checkpoint_data.tracer_vulnerability_reports)
|
||||
# Restore every agent (root + sub-agents) with their last-known status
|
||||
tracer.agents.update(checkpoint_data.tracer_agents)
|
||||
# Restore tool execution records; keys were serialised as str, restore as int
|
||||
for k, v in checkpoint_data.tracer_tool_executions.items():
|
||||
tracer.tool_executions[int(k)] = v
|
||||
# Advance the ID counter so new executions don't overwrite saved ones
|
||||
if checkpoint_data.tracer_next_execution_id > tracer._next_execution_id:
|
||||
tracer._next_execution_id = checkpoint_data.tracer_next_execution_id
|
||||
|
||||
# Added for Resume Feature — show resume banner + replay previous output
|
||||
if is_resuming and checkpoint_data:
|
||||
|
|
|
|||
|
|
@ -711,10 +711,20 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||
|
||||
# Added for Resume Feature — pre-populate tracer with checkpoint data so
|
||||
# stats and findings reflect the full scan history including past sessions.
|
||||
# Also restores sub-agents and their tool executions so the TUI sidebar
|
||||
# shows every agent (completed or in-progress) from the previous session.
|
||||
_cp = getattr(args, "_checkpoint_data", None)
|
||||
if _cp and getattr(args, "resume_from_checkpoint", False):
|
||||
self.tracer.chat_messages.extend(_cp.tracer_chat_messages)
|
||||
self.tracer.vulnerability_reports.extend(_cp.tracer_vulnerability_reports)
|
||||
# Restore full agent registry (root + all sub-agents)
|
||||
self.tracer.agents.update(_cp.tracer_agents)
|
||||
# Restore tool execution records (keys were serialised as str)
|
||||
for k, v in _cp.tracer_tool_executions.items():
|
||||
self.tracer.tool_executions[int(k)] = v
|
||||
# Advance execution ID counter to avoid collisions
|
||||
if _cp.tracer_next_execution_id > self.tracer._next_execution_id:
|
||||
self.tracer._next_execution_id = _cp.tracer_next_execution_id
|
||||
|
||||
self.agent_nodes: dict[str, TreeNode] = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,13 @@ class CheckpointModel(BaseModel):
|
|||
tracer_chat_messages: list[dict[str, Any]] = Field(default_factory=list)
|
||||
tracer_vulnerability_reports: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
# Added for sub-agent persistence: full agent registry and tool execution log
|
||||
# tracer.agents → every agent (root + sub) with status, name, task, parent_id
|
||||
# tracer.tool_executions → every tool call result across all agents
|
||||
tracer_agents: dict[str, Any] = Field(default_factory=dict)
|
||||
tracer_tool_executions: dict[str, Any] = Field(default_factory=dict) # key is str(int)
|
||||
tracer_next_execution_id: int = 1 # restore the ID counter so new IDs don't collide
|
||||
|
||||
# Original scan configuration (passed to execute_scan)
|
||||
scan_config: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
|
@ -100,11 +107,21 @@ class CheckpointManager:
|
|||
|
||||
tracer_chat_messages: list[dict[str, Any]] = []
|
||||
tracer_vulnerability_reports: list[dict[str, Any]] = []
|
||||
tracer_agents: dict[str, Any] = {}
|
||||
tracer_tool_executions: dict[str, Any] = {}
|
||||
tracer_next_execution_id: int = 1
|
||||
if tracer:
|
||||
tracer_chat_messages = list(getattr(tracer, "chat_messages", []))
|
||||
tracer_vulnerability_reports = list(
|
||||
getattr(tracer, "vulnerability_reports", [])
|
||||
)
|
||||
# Added for sub-agent persistence — capture full agent registry
|
||||
# and all tool execution records so sub-agents are fully restored
|
||||
tracer_agents = dict(getattr(tracer, "agents", {}))
|
||||
# tool_executions keys are ints; serialise as strings for JSON
|
||||
raw_execs = getattr(tracer, "tool_executions", {})
|
||||
tracer_tool_executions = {str(k): v for k, v in raw_execs.items()}
|
||||
tracer_next_execution_id = getattr(tracer, "_next_execution_id", 1)
|
||||
|
||||
checkpoint = CheckpointModel(
|
||||
run_name=self.run_name,
|
||||
|
|
@ -114,6 +131,9 @@ class CheckpointManager:
|
|||
agent_state=state_dict,
|
||||
tracer_chat_messages=tracer_chat_messages,
|
||||
tracer_vulnerability_reports=tracer_vulnerability_reports,
|
||||
tracer_agents=tracer_agents,
|
||||
tracer_tool_executions=tracer_tool_executions,
|
||||
tracer_next_execution_id=tracer_next_execution_id,
|
||||
scan_config=scan_config,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue