feat(mma): Implement interception logic in GUI and Conductor
This commit is contained in:
109
gui_2.py
109
gui_2.py
@@ -131,6 +131,29 @@ class MMAApprovalDialog:
|
||||
return self._approved, self._payload
|
||||
|
||||
|
||||
class MMASpawnApprovalDialog:
|
||||
def __init__(self, ticket_id: str, role: str, prompt: str, context_md: str):
|
||||
self._ticket_id = ticket_id
|
||||
self._role = role
|
||||
self._prompt = prompt
|
||||
self._context_md = context_md
|
||||
self._condition = threading.Condition()
|
||||
self._done = False
|
||||
self._approved = False
|
||||
self._abort = False
|
||||
|
||||
def wait(self) -> dict:
|
||||
with self._condition:
|
||||
while not self._done:
|
||||
self._condition.wait(timeout=0.1)
|
||||
return {
|
||||
'approved': self._approved,
|
||||
'abort': self._abort,
|
||||
'prompt': self._prompt,
|
||||
'context_md': self._context_md
|
||||
}
|
||||
|
||||
|
||||
class App:
|
||||
"""The main ImGui interface orchestrator for Manual Slop."""
|
||||
|
||||
@@ -246,6 +269,13 @@ class App:
|
||||
self._mma_approval_edit_mode = False
|
||||
self._mma_approval_payload = ""
|
||||
|
||||
# MMA Spawn approval state
|
||||
self._pending_mma_spawn = None
|
||||
self._mma_spawn_open = False
|
||||
self._mma_spawn_edit_mode = False
|
||||
self._mma_spawn_prompt = ''
|
||||
self._mma_spawn_context = ''
|
||||
|
||||
# Orchestration State
|
||||
self.ui_epic_input = ""
|
||||
self.proposed_tracks: list[dict] = []
|
||||
@@ -989,6 +1019,21 @@ class App:
|
||||
if "dialog_container" in task:
|
||||
task["dialog_container"][0] = dlg
|
||||
|
||||
elif action == "mma_spawn_approval":
|
||||
dlg = MMASpawnApprovalDialog(
|
||||
task.get("ticket_id"),
|
||||
task.get("role"),
|
||||
task.get("prompt"),
|
||||
task.get("context_md")
|
||||
)
|
||||
self._pending_mma_spawn = task
|
||||
self._mma_spawn_prompt = task.get("prompt", "")
|
||||
self._mma_spawn_context = task.get("context_md", "")
|
||||
self._mma_spawn_open = True
|
||||
self._mma_spawn_edit_mode = False
|
||||
if "dialog_container" in task:
|
||||
task["dialog_container"][0] = dlg
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error executing GUI task: {e}")
|
||||
|
||||
@@ -1020,7 +1065,7 @@ class App:
|
||||
else:
|
||||
print("[DEBUG] No pending dialog to reject")
|
||||
|
||||
def _handle_mma_respond(self, approved: bool, payload: str = None):
|
||||
def _handle_mma_respond(self, approved: bool, payload: str = None, abort: bool = False, prompt: str = None, context_md: str = None):
|
||||
if self._pending_mma_approval:
|
||||
dlg = self._pending_mma_approval.get("dialog_container", [None])[0]
|
||||
if dlg:
|
||||
@@ -1031,6 +1076,20 @@ class App:
|
||||
dlg._done = True
|
||||
dlg._condition.notify_all()
|
||||
self._pending_mma_approval = None
|
||||
|
||||
if self._pending_mma_spawn:
|
||||
dlg = self._pending_mma_spawn.get("dialog_container", [None])[0]
|
||||
if dlg:
|
||||
with dlg._condition:
|
||||
dlg._approved = approved
|
||||
dlg._abort = abort
|
||||
if prompt is not None:
|
||||
dlg._prompt = prompt
|
||||
if context_md is not None:
|
||||
dlg._context_md = context_md
|
||||
dlg._done = True
|
||||
dlg._condition.notify_all()
|
||||
self._pending_mma_spawn = None
|
||||
|
||||
def _handle_approve_ask(self):
|
||||
"""Responds with approval for a pending /api/ask request."""
|
||||
@@ -1821,6 +1880,54 @@ class App:
|
||||
imgui.close_current_popup()
|
||||
imgui.end_popup()
|
||||
|
||||
# MMA Spawn Approval Modal
|
||||
if self._pending_mma_spawn:
|
||||
if not self._mma_spawn_open:
|
||||
imgui.open_popup("MMA Spawn Approval")
|
||||
self._mma_spawn_open = True
|
||||
self._mma_spawn_edit_mode = False
|
||||
else:
|
||||
self._mma_spawn_open = False
|
||||
|
||||
if imgui.begin_popup_modal("MMA Spawn Approval", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||
if not self._pending_mma_spawn:
|
||||
imgui.close_current_popup()
|
||||
else:
|
||||
role = self._pending_mma_spawn.get("role", "??")
|
||||
ticket_id = self._pending_mma_spawn.get("ticket_id", "??")
|
||||
imgui.text(f"Spawning {role} for Ticket {ticket_id}")
|
||||
imgui.separator()
|
||||
|
||||
if self._mma_spawn_edit_mode:
|
||||
imgui.text("Edit Prompt:")
|
||||
_, self._mma_spawn_prompt = imgui.input_text_multiline("##spawn_prompt", self._mma_spawn_prompt, imgui.ImVec2(800, 200))
|
||||
imgui.text("Edit Context MD:")
|
||||
_, self._mma_spawn_context = imgui.input_text_multiline("##spawn_context", self._mma_spawn_context, imgui.ImVec2(800, 300))
|
||||
else:
|
||||
imgui.text("Proposed Prompt:")
|
||||
imgui.begin_child("spawn_prompt_preview", imgui.ImVec2(800, 150), True)
|
||||
imgui.text_unformatted(self._mma_spawn_prompt)
|
||||
imgui.end_child()
|
||||
imgui.text("Proposed Context MD:")
|
||||
imgui.begin_child("spawn_context_preview", imgui.ImVec2(800, 250), True)
|
||||
imgui.text_unformatted(self._mma_spawn_context)
|
||||
imgui.end_child()
|
||||
|
||||
imgui.separator()
|
||||
if imgui.button("Approve", imgui.ImVec2(120, 0)):
|
||||
self._handle_mma_respond(approved=True, prompt=self._mma_spawn_prompt, context_md=self._mma_spawn_context)
|
||||
imgui.close_current_popup()
|
||||
|
||||
imgui.same_line()
|
||||
if imgui.button("Edit Mode" if not self._mma_spawn_edit_mode else "Preview Mode", imgui.ImVec2(120, 0)):
|
||||
self._mma_spawn_edit_mode = not self._mma_spawn_edit_mode
|
||||
|
||||
imgui.same_line()
|
||||
if imgui.button("Abort", imgui.ImVec2(120, 0)):
|
||||
self._handle_mma_respond(approved=False, abort=True)
|
||||
imgui.close_current_popup()
|
||||
imgui.end_popup()
|
||||
|
||||
if self.show_script_output:
|
||||
if self._trigger_script_blink:
|
||||
self._trigger_script_blink = False
|
||||
|
||||
@@ -217,22 +217,27 @@ def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.A
|
||||
start = time.time()
|
||||
while dialog_container[0] is None and time.time() - start < 60:
|
||||
time.sleep(0.1)
|
||||
|
||||
if dialog_container[0]:
|
||||
approved, final_payload = dialog_container[0].wait()
|
||||
|
||||
# Extract modifications from final_payload if it's a dict
|
||||
modified_prompt = prompt
|
||||
modified_context = context_md
|
||||
|
||||
if isinstance(final_payload, dict):
|
||||
modified_prompt = final_payload.get("prompt", prompt)
|
||||
modified_context = final_payload.get("context_md", context_md)
|
||||
|
||||
return approved, modified_prompt, modified_context
|
||||
|
||||
return False, prompt, context_md
|
||||
|
||||
if dialog_container[0]:
|
||||
res = dialog_container[0].wait()
|
||||
|
||||
if isinstance(res, dict):
|
||||
approved = res.get("approved", False)
|
||||
abort = res.get("abort", False)
|
||||
modified_prompt = res.get("prompt", prompt)
|
||||
modified_context = res.get("context_md", context_md)
|
||||
return approved and not abort, modified_prompt, modified_context
|
||||
else:
|
||||
# Fallback for old tuple style if any
|
||||
approved, final_payload = res
|
||||
modified_prompt = prompt
|
||||
modified_context = context_md
|
||||
if isinstance(final_payload, dict):
|
||||
modified_prompt = final_payload.get("prompt", prompt)
|
||||
modified_context = final_payload.get("context_md", context_md)
|
||||
return approved, modified_prompt, modified_context
|
||||
|
||||
return False, prompt, context_md
|
||||
def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: List[str] = None, event_queue: events.AsyncEventQueue = None, engine: Optional['ConductorEngine'] = None, md_content: str = ""):
|
||||
"""
|
||||
Simulates the lifecycle of a single agent working on a ticket.
|
||||
|
||||
@@ -11,7 +11,11 @@ class MockDialog:
|
||||
self.approved = approved
|
||||
self.final_payload = final_payload
|
||||
def wait(self):
|
||||
return self.approved, self.final_payload
|
||||
# Match the new return format: a dictionary
|
||||
res = {'approved': self.approved, 'abort': False}
|
||||
if self.final_payload:
|
||||
res.update(self.final_payload)
|
||||
return res
|
||||
|
||||
@pytest.fixture
|
||||
def mock_ai_client():
|
||||
|
||||
Reference in New Issue
Block a user