fix(mma): Unblock visual simulation - event routing, loop passing, adapter preservation

Three independent root causes fixed:
- gui_2.py: Route mma_spawn_approval/mma_step_approval events in _process_event_queue
- multi_agent_conductor.py: Pass asyncio loop from ConductorEngine.run() through to
  thread-pool workers for thread-safe event queue access; add _queue_put helper
- ai_client.py: Preserve GeminiCliAdapter in reset_session() instead of nulling it

Test: visual_sim_mma_v2::test_mma_complete_lifecycle passes in ~8s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 08:32:31 -05:00
parent db32a874fd
commit da21ed543d
11 changed files with 144 additions and 122 deletions

View File

@@ -388,6 +388,7 @@ class App:
'btn_mma_accept_tracks': self._cb_accept_tracks,
'btn_mma_start_track': self._cb_start_track,
'btn_approve_tool': self._handle_approve_tool,
'btn_approve_mma_step': self._handle_approve_mma_step,
'btn_approve_spawn': self._handle_approve_spawn,
}
self._predefined_callbacks: dict[str, Callable[..., Any]] = {
@@ -880,8 +881,17 @@ class App:
self.mma_status = payload.get("status", "idle")
self.active_tier = payload.get("active_tier")
self.mma_tier_usage = payload.get("tier_usage", self.mma_tier_usage)
self.active_track = payload.get("track")
self.active_tickets = payload.get("tickets", [])
track_data = payload.get("track")
if track_data:
tickets = []
for t_data in self.active_tickets:
tickets.append(Ticket(**t_data))
self.active_track = Track(
id=track_data.get("id"),
description=track_data.get("title", ""),
tickets=tickets
)
elif action == "set_value":
item = task.get("item")
value = task.get("value")
@@ -996,6 +1006,16 @@ class App:
else:
print("[DEBUG] No pending tool approval found")
def _handle_approve_mma_step(self) -> None:
"""Logic for approving a pending MMA step execution via API hooks."""
print("[DEBUG] _handle_approve_mma_step called")
if self._pending_mma_approval:
self._handle_mma_respond(approved=True, payload=self._mma_approval_payload)
self._mma_approval_open = False
self._pending_mma_approval = None
else:
print("[DEBUG] No pending MMA step approval found")
def _handle_approve_spawn(self) -> None:
"""Logic for approving a pending sub-agent spawn via API hooks."""
print("[DEBUG] _handle_approve_spawn called")
@@ -1162,6 +1182,11 @@ class App:
"action": "mma_state_update",
"payload": payload
})
elif event_name in ("mma_spawn_approval", "mma_step_approval"):
# Route approval events to GUI tasks — payload already has the
# correct structure for _process_pending_gui_tasks handlers.
with self._pending_gui_tasks_lock:
self._pending_gui_tasks.append(payload)
def _handle_request_event(self, event: events.UserRequestEvent) -> None:
"""Processes a UserRequestEvent by calling the AI client."""
@@ -2031,7 +2056,7 @@ class App:
if self.active_track:
# Use the active track object directly to start execution
self.mma_status = "running"
engine = multi_agent_conductor.ConductorEngine(self.active_track, self.event_queue)
engine = multi_agent_conductor.ConductorEngine(self.active_track, self.event_queue, auto_queue=not self.mma_step_mode)
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=self.active_track.id)
full_md, _, _ = aggregate.run(flat)
asyncio.run_coroutine_threadsafe(engine.run(md_content=full_md), self._loop)
@@ -2108,7 +2133,7 @@ class App:
state = TrackState(metadata=meta, discussion=[], tasks=tickets)
project_manager.save_track_state(track_id, state, self.ui_files_base_dir)
# 4. Initialize ConductorEngine and run loop
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue)
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue, auto_queue=not self.mma_step_mode)
# Use current full markdown context for the track execution
track_id_param = track.id
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id_param)