Files
manual_slop/src/events.py
T

196 lines
26 KiB
Python

"""
Events - Decoupled event emission and queuing for cross-thread communication.
This module provides three complementary patterns for thread-safe communication
between the GUI main thread and background workers:
1. EventEmitter: Pub/sub pattern for synchronous event broadcast
- Used for: API lifecycle events (request_start, response_received, tool_execution)
- Thread-safe: Callbacks execute on emitter's thread
- Example: ai_client.py emits 'request_start' and 'response_received' events
2. AsyncEventQueue: Producer-consumer pattern via queue.Queue
- Used for: Decoupled task submission where consumer polls at its own pace
- Thread-safe: Built on Python's thread-safe queue.Queue
- Example: Background workers submit tasks, main thread drains queue
3. UserRequestEvent: Structured payload for AI request data
- Used for: Bundling prompt, context, files, and base_dir into single object
- Immutable data transfer object for cross-thread handoff
Integration Points:
- ai_client.py: EventEmitter for API lifecycle events
- gui_2.py: Consumes events via _process_event_queue()
- multi_agent_conductor.py: Uses AsyncEventQueue for state updates
- api_hooks.py: Pushes events to _api_event_queue for external visibility
Thread Safety:
- EventEmitter: NOT thread-safe for concurrent on/emit (use from single thread)
- AsyncEventQueue: FULLY thread-safe (built on queue.Queue)
- UserRequestEvent: Immutable, safe for concurrent access
"""
import queue
from typing import Callable, Any, Dict, List, Tuple, Optional
from pathlib import Path
class EventEmitter:
"""
Simple event emitter for decoupled communication between modules.
"""
def __init__(self) -> None:
"""
Initializes the EventEmitter with an empty listener map.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._listeners: Dict[str, List[Callable[..., Any]]] = {}
def on(self, event_name: str, callback: Callable[..., Any]) -> None:
"""
Registers a callback for a specific event.
Args:
event_name: The name of the event to listen for.
callback: The function to call when the event is emitted.
[C: tests/test_api_events.py:test_event_emission, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events]
"""
if event_name not in self._listeners:
self._listeners[event_name] = []
self._listeners[event_name].append(callback)
def emit(self, event_name: str, *args: Any, **kwargs: Any) -> None:
"""
Emits an event, calling all registered callbacks.
Args:
event_name: The name of the event to emit.
*args: Positional arguments to pass to callbacks.
**kwargs: Keyword arguments to pass to callbacks.
[C: tests/test_api_events.py:test_event_emission]
"""
if event_name in self._listeners:
for callback in self._listeners[event_name]:
callback(*args, **kwargs)
def clear(self) -> None:
"""
Clears all registered listeners.
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._render_tool_calls_panel, src/gui_2.py:App._show_menus, src/history.py:HistoryManager.push, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/markdown_helper.py:MarkdownRenderer.clear_cache, src/multi_agent_conductor.py:ConductorEngine.resume, src/multi_agent_conductor.py:WorkerPool.join_all, src/paths.py:reset_resolved, src/summary_cache.py:SummaryCache.clear, tests/conftest.py:reset_ai_client]
"""
self._listeners.clear()
class AsyncEventQueue:
"""
Synchronous event queue for decoupled communication using queue.Queue.
(Named AsyncEventQueue for architectural consistency, but is synchronous)
"""
def __init__(self) -> None:
"""
Initializes the AsyncEventQueue with an internal queue.Queue.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._queue: queue.Queue[Tuple[str, Any]] = queue.Queue()
self.websocket_server: Optional[Any] = None
def put(self, event_name: str, payload: Any = None) -> None:
"""
Puts an event into the queue.
Args:
event_name: The name of the event.
payload: Optional data associated with the event.
[C: src/gui_2.py:App._render_external_tools_panel, src/gui_2.py:App._render_log_management, src/gui_2.py:App._render_rag_panel, src/gui_2.py:App._render_token_budget_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:ConductorEngine._push_state, src/multi_agent_conductor.py:_queue_put, tests/test_gui_events_v2.py:test_sync_event_queue, tests/test_sync_events.py:test_sync_event_queue_multiple, tests/test_sync_events.py:test_sync_event_queue_none_payload, tests/test_sync_events.py:test_sync_event_queue_put_get]
"""
self._queue.put((event_name, payload))
if self.websocket_server:
# Ensure payload is JSON serializable for websocket broadcast
serializable_payload = payload
if hasattr(payload, 'to_dict'):
serializable_payload = payload.to_dict()
elif hasattr(payload, '__dict__'):
serializable_payload = vars(payload)
self.websocket_server.broadcast("events", {"event": event_name, "payload": serializable_payload})
def get(self) -> Tuple[str, Any]:
"""
Gets an event from the queue.
Returns:
A tuple containing (event_name, payload).
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/sim_tools.py:ToolsSimulation.run, simulation/user_agent.py:UserSimAgent.generate_response, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, src/external_editor.py:ExternalEditorLauncher.get_editor, src/external_editor.py:get_default_launcher, src/external_editor.py:resolve_project_editor_override, src/gemini_cli_adapter.py:GeminiCliAdapter.send, src/gui_2.py:App.__init__, src/gui_2.py:App._cb_block_ticket, src/gui_2.py:App._cb_unblock_ticket, src/gui_2.py:App._gui_func, src/gui_2.py:App._handle_history_logic, src/gui_2.py:App._render_cache_panel, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_context_composition_panel, src/gui_2.py:App._render_context_presets_panel, src/gui_2.py:App._render_diagnostics_panel, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_external_editor_panel, src/gui_2.py:App._render_external_tools_panel, src/gui_2.py:App._render_history_window, src/gui_2.py:App._render_log_management, src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_persona_editor_window, src/gui_2.py:App._render_persona_selector_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_session_insights_panel, src/gui_2.py:App._render_shader_live_editor, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App._render_synthesis_panel, src/gui_2.py:App._render_takes_panel, src/gui_2.py:App._render_task_dag_panel, src/gui_2.py:App._render_thinking_trace, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._render_tier_stream_panel, src/gui_2.py:App._render_token_budget_panel, src/gui_2.py:App._render_tool_analytics_panel, src/gui_2.py:App._render_tool_calls_panel, src/gui_2.py:App._render_tool_preset_manager_content, src/gui_2.py:App._render_track_proposal_modal, src/gui_2.py:App._reorder_ticket, src/gui_2.py:App.bulk_block, src/gui_2.py:App.bulk_execute, src/gui_2.py:App.bulk_skip, src/gui_2.py:App.load_context_preset, src/gui_2.py:App.render_path_field, src/gui_2.py:App.run, src/gui_2.py:truncate_entries, src/history.py:UISnapshot.from_dict, src/log_registry.py:LogRegistry.get_old_non_whitelisted_sessions, src/log_registry.py:LogRegistry.is_session_whitelisted, src/log_registry.py:LogRegistry.update_auto_whitelist_status, src/log_registry.py:LogRegistry.update_session_metadata, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/mcp_client.py:StdioMCPServer._send_request, src/mcp_client.py:StdioMCPServer.call_tool, src/mcp_client.py:_DDGParser.handle_starttag, src/mcp_client.py:configure, src/mcp_client.py:dispatch, src/mcp_client.py:get_tool_schemas, src/models.py:BiasProfile.from_dict, src/models.py:ExternalEditorConfig.from_dict, src/models.py:FileItem.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:MCPServerConfig.from_dict, src/models.py:Metadata.from_dict, src/models.py:Persona.from_dict, src/models.py:Persona.max_output_tokens, src/models.py:Persona.model, src/models.py:Persona.provider, src/models.py:Persona.temperature, src/models.py:Persona.top_p, src/models.py:Preset.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:TextEditorConfig.from_dict, src/models.py:Ticket.from_dict, src/models.py:Tool.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:VectorStoreConfig.from_dict, src/models.py:WorkspaceProfile.from_dict, src/models.py:save_config, src/multi_agent_conductor.py:ConductorEngine.__init__, src/multi_agent_conductor.py:ConductorEngine.kill_worker, src/multi_agent_conductor.py:ConductorEngine.parse_json_tickets, src/multi_agent_conductor.py:ConductorEngine.run, src/multi_agent_conductor.py:clutch_callback, src/multi_agent_conductor.py:confirm_spawn, src/multi_agent_conductor.py:run_worker_lifecycle, src/multi_agent_conductor.py:worker_comms_callback, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, src/orchestrator_pm.py:get_track_history_summary, src/orchestrator_pm.py:module, src/paths.py:_get_project_conductor_dir_from_toml, src/paths.py:get_config_path, src/paths.py:get_global_personas_path, src/paths.py:get_global_presets_path, src/paths.py:get_global_tool_presets_path, src/paths.py:get_global_workspace_profiles_path, src/performance_monitor.py:PerformanceMonitor._get_avg, src/performance_monitor.py:PerformanceMonitor.end_component, src/performance_monitor.py:PerformanceMonitor.get_metrics, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/project_manager.py:branch_discussion, src/project_manager.py:entry_to_str, src/project_manager.py:flat_config, src/project_manager.py:get_all_tracks, src/project_manager.py:load_track_history, src/project_manager.py:migrate_from_legacy_config, src/project_manager.py:promote_take, src/rag_engine.py:RAGEngine.get_all_indexed_paths, src/rag_engine.py:RAGEngine.index_file, src/shell_runner.py:_build_subprocess_env, src/shell_runner.py:_load_env_config, src/summarize.py:build_summary_markdown, src/summarize.py:summarise_file, src/summarize.py:summarise_items, src/summary_cache.py:SummaryCache.get_summary, src/synthesis_formatter.py:format_takes_diff, src/theme.py:apply, src/theme.py:get_palette_colours, src/theme.py:get_shader_config, src/theme.py:get_window_frame_config, src/theme.py:load_from_config, src/theme_2.py:load_from_config, src/tool_bias.py:ToolBiasEngine.apply_semantic_nudges, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/conftest.py:live_gui, tests/mock_gemini_cli.py:main, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_async_tools.py:mocked_async_dispatch, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_conductor_engine_v2.py:mock_open_side_effect, tests/test_conductor_engine_v2.py:mock_send_side_effect, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_events_v2.py:test_sync_event_queue, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_phase4.py:test_delete_ticket_logic, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_headless_service.py:TestHeadlessAPI.test_endpoint_no_api_key_configured, tests/test_headless_service.py:TestHeadlessAPI.test_get_context_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_health_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_list_sessions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_pending_actions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_authorized, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_unauthorized, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow, tests/test_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_log_registry.py:TestLogRegistry.test_register_session, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_not_applied_for_prior_session, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_tier3_only, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_all, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_excludes_none_tier, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_tier3_only, tests/test_mma_approval_indicators.py:_make_app, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:_poll_mma_workers, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_dashboard_streams.py:_make_app, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, tests/test_mma_step_mode_sim.py:_poll_mma_status, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_mock_gemini_cli.py:get_message_content, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_phase6_engine.py:test_worker_streaming_intermediate, tests/test_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_sim_ai_settings.py:side_effect, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_status_encapsulation.py:test_status_properties, tests/test_sync_events.py:test_sync_event_queue_multiple, tests/test_sync_events.py:test_sync_event_queue_none_payload, tests/test_sync_events.py:test_sync_event_queue_put_get, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tiered_aggregation.py:test_app_controller_do_generate_uses_persona_strategy, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_token_usage.py:test_token_usage_tracking, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_ui_summary_only_removal.py:test_project_without_summary_only_loads, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:_mma_active, tests/test_visual_sim_mma_v2.py:_poll, tests/test_visual_sim_mma_v2.py:_tier3_in_streams, tests/test_visual_sim_mma_v2.py:_tier3_usage_nonzero, tests/test_visual_sim_mma_v2.py:_track_loaded, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle]
"""
return self._queue.get()
def empty(self) -> bool:
"""
Checks if the queue is empty.
Returns:
True if the queue is empty, False otherwise.
[C: tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow]
"""
return self._queue.empty()
def task_done(self) -> None:
"""Signals that a formerly enqueued task is complete."""
self._queue.task_done()
def join(self) -> None:
"""
Blocks until all items in the queue have been gotten and processed.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:module, simulation/sim_base.py:module, src/file_cache.py:ASTParser.get_code_outline, src/gemini_cli_adapter.py:GeminiCliAdapter.count_tokens, src/gemini_cli_adapter.py:GeminiCliAdapter.send, src/gui_2.py:App._render_beads_tab, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_mma_dashboard, src/log_pruner.py:LogPruner.prune, src/log_registry.py:LogRegistry.update_auto_whitelist_status, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/mcp_client.py:StdioMCPServer.call_tool, src/mcp_client.py:_resolve_and_check, src/mcp_client.py:derive_code_path, src/mcp_client.py:dispatch, src/mcp_client.py:fetch_url, src/mcp_client.py:get_file_slice, src/mcp_client.py:get_tree, src/mcp_client.py:list_directory, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_class_summary, src/mcp_client.py:py_get_definition, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:py_get_imports, src/mcp_client.py:py_get_signature, src/mcp_client.py:py_get_symbol_info, src/mcp_client.py:py_get_var_declaration, src/mcp_client.py:search_files, src/mcp_client.py:set_file_slice, src/mcp_client.py:web_search, src/models.py:parse_history_entries, src/multi_agent_conductor.py:ConductorEngine.kill_worker, src/multi_agent_conductor.py:WorkerPool.join_all, src/orchestrator_pm.py:generate_tracks, src/orchestrator_pm.py:get_track_history_summary, src/outline_tool.py:CodeOutliner.outline, src/performance_monitor.py:PerformanceMonitor.stop, src/project_manager.py:str_to_entry, src/rag_engine.py:RAGEngine._init_vector_store, src/rag_engine.py:RAGEngine.index_file, src/session_logger.py:open_session, src/shell_runner.py:_build_subprocess_env, src/shell_runner.py:run_powershell, src/summarize.py:_summarise_generic, src/summarize.py:_summarise_markdown, src/summarize.py:_summarise_python, src/summarize.py:_summarise_toml, src/summarize.py:build_summary_markdown, src/synthesis_formatter.py:format_takes_diff, src/tool_bias.py:ToolBiasEngine.generate_tooling_strategy, tests/conftest.py:live_gui, tests/conftest.py:module, tests/mock_gemini_cli.py:main, tests/smoke_status_hook.py:module, tests/test_agent_capabilities.py:module, tests/test_agent_tools_wiring.py:module, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_api_hook_client.py:module, tests/test_api_hook_extensions.py:module, tests/test_arch_boundary_phase1.py:module, tests/test_arch_boundary_phase2.py:module, tests/test_arch_boundary_phase3.py:module, tests/test_auto_switch_sim.py:module, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:module, tests/test_context_pruner.py:test_performance_large_file, tests/test_context_pruner.py:test_token_reduction_logging, tests/test_deepseek_infra.py:module, tests/test_extended_sims.py:module, tests/test_external_editor_gui.py:module, tests/test_gemini_metrics.py:module, tests/test_gui2_parity.py:module, tests/test_gui2_performance.py:module, tests/test_gui_diagnostics.py:module, tests/test_gui_performance_requirements.py:module, tests/test_gui_stress_performance.py:module, tests/test_gui_updates.py:module, tests/test_headless_service.py:module, tests/test_history_management.py:module, tests/test_hooks.py:module, tests/test_layout_reorganization.py:module, tests/test_live_workflow.py:module, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.setUp, tests/test_mcp_perf_tool.py:module, tests/test_mcp_ts_integration.py:module, tests/test_mma_approval_indicators.py:_collect_text_colored_args, tests/test_mma_concurrent_tracks_sim.py:module, tests/test_mma_concurrent_tracks_stress_sim.py:module, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders, tests/test_mma_step_mode_sim.py:module, tests/test_patch_modal_gui.py:module, tests/test_performance_monitor.py:module, tests/test_rag_integration.py:mock_project, tests/test_rag_integration.py:test_rag_integration, tests/test_rag_phase4_final_verify.py:module, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:module, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:module, tests/test_selectable_ui.py:module, tests/test_sim_ai_settings.py:module, tests/test_sim_base.py:module, tests/test_sim_context.py:module, tests/test_sim_execution.py:module, tests/test_sim_tools.py:module, tests/test_skeleton_injection.py:test_update_inject_preview_truncation, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_system_prompt_exposure.py:module, tests/test_system_prompt_sim.py:module, tests/test_token_usage.py:module, tests/test_tool_management_layout.py:module, tests/test_ts_c_tools.py:module, tests/test_ts_cpp_tools.py:module, tests/test_ui_cache_controls_sim.py:module, tests/test_undo_redo_sim.py:module, tests/test_user_agent.py:module, tests/test_visual_orchestration.py:module, tests/test_visual_sim_gui_ux.py:module, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:module, tests/test_workflow_sim.py:module, tests/test_workspace_profiles_sim.py:module]
"""
self._queue.join()
# Alias for backward compatibility
SyncEventQueue = AsyncEventQueue
class UserRequestEvent:
"""
Payload for a user request event.
"""
def __init__(self, prompt: str, stable_md: str, file_items: List[Any], disc_text: str, base_dir: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.prompt = prompt
self.stable_md = stable_md
self.file_items = file_items
self.disc_text = disc_text
self.base_dir = base_dir
def to_dict(self) -> Dict[str, Any]:
"""
[C: src/gui_2.py:App._take_snapshot, src/gui_2.py:App.save_context_preset, src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
"""
def _make_serializable(obj: Any) -> Any:
if isinstance(obj, dict):
return {k: _make_serializable(v) for k, v in obj.items()}
if isinstance(obj, list):
return [_make_serializable(x) for x in obj]
if isinstance(obj, Path):
return str(obj)
if hasattr(obj, 'to_dict'):
return obj.to_dict()
if not isinstance(obj, (str, int, float, bool, type(None))):
return str(obj)
return obj
return {
"prompt": self.prompt,
"stable_md": self.stable_md,
"file_items": _make_serializable(self.file_items),
"disc_text": self.disc_text,
"base_dir": str(self.base_dir)
}