Showing preview only (9,224K chars total). Download the full file or copy to clipboard to get everything.
Repository: microsoft/UFO
Branch: main
Commit: aceffa9403fa
Files: 815
Total size: 8.7 MB
Directory structure:
gitextract_4l6r5una/
├── .github/
│ └── workflows/
│ └── document_deploy.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DISCLAIMER.md
├── LICENSE
├── README.md
├── README_ZH.md
├── SECURITY.md
├── aip/
│ ├── __init__.py
│ ├── endpoints/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── client_endpoint.py
│ │ ├── constellation_endpoint.py
│ │ └── server_endpoint.py
│ ├── extensions/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ └── middleware.py
│ ├── messages.py
│ ├── protocol/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── command.py
│ │ ├── device_info.py
│ │ ├── heartbeat.py
│ │ ├── registration.py
│ │ └── task_execution.py
│ ├── resilience/
│ │ ├── __init__.py
│ │ ├── heartbeat_manager.py
│ │ ├── reconnection.py
│ │ └── timeout.py
│ └── transport/
│ ├── __init__.py
│ ├── adapters.py
│ ├── base.py
│ └── websocket.py
├── config/
│ ├── __init__.py
│ ├── config_loader.py
│ ├── config_schemas.py
│ ├── galaxy/
│ │ ├── agent.yaml.template
│ │ ├── constellation.yaml
│ │ └── devices.yaml
│ └── ufo/
│ ├── agents.yaml.template
│ ├── mcp.yaml
│ ├── prices.yaml
│ ├── rag.yaml
│ ├── system.yaml
│ └── third_party.yaml
├── dataflow/
│ ├── .gitignore
│ ├── README.md
│ ├── __main__.py
│ ├── config/
│ │ ├── config.py
│ │ ├── config.yaml.template
│ │ └── config_dev.yaml
│ ├── data_flow_controller.py
│ ├── dataflow.py
│ ├── env/
│ │ └── env_manager.py
│ ├── execution/
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── execute_agent.py
│ │ │ └── execute_eval_agent.py
│ │ └── workflow/
│ │ ├── __init__.py
│ │ └── execute_flow.py
│ ├── instantiation/
│ │ ├── __init__.py
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── filter_agent.py
│ │ │ ├── prefill_agent.py
│ │ │ └── template_agent.py
│ │ └── workflow/
│ │ ├── __init__.py
│ │ ├── choose_template_flow.py
│ │ ├── filter_flow.py
│ │ └── prefill_flow.py
│ ├── prompter/
│ │ ├── __init__.py
│ │ ├── execution/
│ │ │ ├── __init__.py
│ │ │ └── execute_eval_prompter.py
│ │ └── instantiation/
│ │ ├── __init__.py
│ │ ├── filter_prompter.py
│ │ ├── prefill_prompter.py
│ │ └── template_prompter.py
│ ├── prompts/
│ │ └── instantiation/
│ │ └── visual/
│ │ ├── filter.yaml
│ │ ├── prefill.yaml
│ │ ├── prefill_example.yaml
│ │ └── template.yaml
│ ├── schema/
│ │ ├── execution_schema.json
│ │ └── instantiation_schema.json
│ └── templates/
│ └── word/
│ ├── 1.docx
│ ├── 2.docx
│ ├── 3.docx
│ ├── 4.docx
│ ├── 5.docx
│ ├── 6.docx
│ ├── 7.docx
│ ├── description.json
│ ├── template1.docx
│ └── template2.docx
├── documents/
│ ├── docs/
│ │ ├── about/
│ │ │ ├── CODE_OF_CONDUCT.md
│ │ │ ├── CONTRIBUTING.md
│ │ │ ├── DISCLAIMER.md
│ │ │ ├── LICENSE.md
│ │ │ └── SUPPORT.md
│ │ ├── aip/
│ │ │ ├── endpoints.md
│ │ │ ├── messages.md
│ │ │ ├── overview.md
│ │ │ ├── protocols.md
│ │ │ ├── resilience.md
│ │ │ └── transport.md
│ │ ├── choose_path.md
│ │ ├── client/
│ │ │ ├── computer.md
│ │ │ ├── computer_manager.md
│ │ │ ├── device_info.md
│ │ │ ├── mcp_integration.md
│ │ │ ├── overview.md
│ │ │ ├── quick_start.md
│ │ │ ├── ufo_client.md
│ │ │ └── websocket_client.md
│ │ ├── configuration/
│ │ │ ├── models/
│ │ │ │ ├── azure_openai.md
│ │ │ │ ├── claude.md
│ │ │ │ ├── custom_model.md
│ │ │ │ ├── deepseek.md
│ │ │ │ ├── gemini.md
│ │ │ │ ├── ollama.md
│ │ │ │ ├── openai.md
│ │ │ │ ├── operator.md
│ │ │ │ ├── overview.md
│ │ │ │ └── qwen.md
│ │ │ └── system/
│ │ │ ├── agents_config.md
│ │ │ ├── extending.md
│ │ │ ├── galaxy_agent.md
│ │ │ ├── galaxy_constellation.md
│ │ │ ├── galaxy_devices.md
│ │ │ ├── mcp_reference.md
│ │ │ ├── migration.md
│ │ │ ├── overview.md
│ │ │ ├── prices_config.md
│ │ │ ├── rag_config.md
│ │ │ ├── system_config.md
│ │ │ └── third_party_config.md
│ │ ├── faq.md
│ │ ├── galaxy/
│ │ │ ├── agent_registration/
│ │ │ │ ├── agent_profile.md
│ │ │ │ ├── device_registry.md
│ │ │ │ ├── overview.md
│ │ │ │ └── registration_flow.md
│ │ │ ├── client/
│ │ │ │ ├── aip_integration.md
│ │ │ │ ├── components.md
│ │ │ │ ├── constellation_client.md
│ │ │ │ ├── device_manager.md
│ │ │ │ ├── galaxy_client.md
│ │ │ │ └── overview.md
│ │ │ ├── constellation/
│ │ │ │ ├── constellation_editor.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── task_constellation.md
│ │ │ │ ├── task_star.md
│ │ │ │ └── task_star_line.md
│ │ │ ├── constellation_agent/
│ │ │ │ ├── command.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── state.md
│ │ │ │ └── strategy.md
│ │ │ ├── constellation_orchestrator/
│ │ │ │ ├── api_reference.md
│ │ │ │ ├── asynchronous_scheduling.md
│ │ │ │ ├── batched_editing.md
│ │ │ │ ├── consistency_guarantees.md
│ │ │ │ ├── constellation_manager.md
│ │ │ │ ├── event_driven_coordination.md
│ │ │ │ ├── overview.md
│ │ │ │ └── safe_assignment_locking.md
│ │ │ ├── evaluation/
│ │ │ │ ├── performance_metrics.md
│ │ │ │ ├── result_json.md
│ │ │ │ └── trajectory_report.md
│ │ │ ├── observer/
│ │ │ │ ├── agent_output_observer.md
│ │ │ │ ├── event_system.md
│ │ │ │ ├── metrics_observer.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── progress_observer.md
│ │ │ │ ├── synchronizer.md
│ │ │ │ └── visualization_observer.md
│ │ │ ├── overview.md
│ │ │ └── webui.md
│ │ ├── getting_started/
│ │ │ ├── migration_ufo2_to_galaxy.md
│ │ │ ├── more_guidance.md
│ │ │ ├── quick_start_galaxy.md
│ │ │ ├── quick_start_linux.md
│ │ │ ├── quick_start_mobile.md
│ │ │ └── quick_start_ufo2.md
│ │ ├── index.md
│ │ ├── infrastructure/
│ │ │ ├── agents/
│ │ │ │ ├── agent_types.md
│ │ │ │ ├── design/
│ │ │ │ │ ├── blackboard.md
│ │ │ │ │ ├── command.md
│ │ │ │ │ ├── memory.md
│ │ │ │ │ ├── processor.md
│ │ │ │ │ ├── prompter.md
│ │ │ │ │ ├── state.md
│ │ │ │ │ └── strategy.md
│ │ │ │ ├── overview.md
│ │ │ │ └── server_client_architecture.md
│ │ │ └── modules/
│ │ │ ├── context.md
│ │ │ ├── dispatcher.md
│ │ │ ├── overview.md
│ │ │ ├── platform_sessions.md
│ │ │ ├── round.md
│ │ │ ├── session.md
│ │ │ └── session_pool.md
│ │ ├── javascripts/
│ │ │ └── mermaid-init.js
│ │ ├── linux/
│ │ │ ├── as_galaxy_device.md
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── mcp/
│ │ │ ├── action.md
│ │ │ ├── configuration.md
│ │ │ ├── data_collection.md
│ │ │ ├── local_servers.md
│ │ │ ├── overview.md
│ │ │ ├── remote_servers.md
│ │ │ └── servers/
│ │ │ ├── app_ui_executor.md
│ │ │ ├── bash_executor.md
│ │ │ ├── command_line_executor.md
│ │ │ ├── constellation_editor.md
│ │ │ ├── excel_com_executor.md
│ │ │ ├── hardware_executor.md
│ │ │ ├── host_ui_executor.md
│ │ │ ├── mobile_executor.md
│ │ │ ├── pdf_reader_executor.md
│ │ │ ├── ppt_com_executor.md
│ │ │ ├── ui_collector.md
│ │ │ └── word_com_executor.md
│ │ ├── mobile/
│ │ │ ├── as_galaxy_device.md
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── project_directory_structure.md
│ │ ├── server/
│ │ │ ├── api.md
│ │ │ ├── client_connection_manager.md
│ │ │ ├── monitoring.md
│ │ │ ├── overview.md
│ │ │ ├── quick_start.md
│ │ │ ├── session_manager.md
│ │ │ └── websocket_handler.md
│ │ ├── tutorials/
│ │ │ ├── creating_app_agent/
│ │ │ │ ├── demonstration_provision.md
│ │ │ │ ├── help_document_provision.md
│ │ │ │ ├── overview.md
│ │ │ │ └── warpping_app_native_api.md
│ │ │ ├── creating_device_agent/
│ │ │ │ ├── client_setup.md
│ │ │ │ ├── configuration.md
│ │ │ │ ├── core_components.md
│ │ │ │ ├── example_mobile_agent.md
│ │ │ │ ├── index.md
│ │ │ │ ├── mcp_server.md
│ │ │ │ ├── overview.md
│ │ │ │ └── testing.md
│ │ │ ├── creating_mcp_servers.md
│ │ │ └── creating_third_party_agents.md
│ │ └── ufo2/
│ │ ├── advanced_usage/
│ │ │ ├── batch_mode.md
│ │ │ ├── customization.md
│ │ │ ├── follower_mode.md
│ │ │ └── operator_as_app_agent.md
│ │ ├── app_agent/
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── as_galaxy_device.md
│ │ ├── core_features/
│ │ │ ├── control_detection/
│ │ │ │ ├── hybrid_detection.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── uia_detection.md
│ │ │ │ └── visual_detection.md
│ │ │ ├── hybrid_actions.md
│ │ │ ├── knowledge_substrate/
│ │ │ │ ├── experience_learning.md
│ │ │ │ ├── learning_from_bing_search.md
│ │ │ │ ├── learning_from_demonstration.md
│ │ │ │ ├── learning_from_help_document.md
│ │ │ │ └── overview.md
│ │ │ └── multi_action.md
│ │ ├── dataflow/
│ │ │ ├── execution.md
│ │ │ ├── instantiation.md
│ │ │ ├── overview.md
│ │ │ ├── result.md
│ │ │ └── windows_app_env.md
│ │ ├── evaluation/
│ │ │ ├── benchmark/
│ │ │ │ ├── osworld.md
│ │ │ │ ├── overview.md
│ │ │ │ └── windows_agent_arena.md
│ │ │ ├── evaluation_agent.md
│ │ │ └── logs/
│ │ │ ├── evaluation_logs.md
│ │ │ ├── markdown_log_viewer.md
│ │ │ ├── overview.md
│ │ │ ├── request_logs.md
│ │ │ ├── screenshots_logs.md
│ │ │ ├── step_logs.md
│ │ │ └── ui_tree_logs.md
│ │ ├── host_agent/
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── overview.md
│ │ └── prompts/
│ │ ├── basic_template.md
│ │ ├── examples_prompts.md
│ │ └── overview.md
│ └── mkdocs.yml
├── galaxy/
│ ├── README.md
│ ├── README_ZH.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── constellation_agent.py
│ │ ├── constellation_agent_states.py
│ │ ├── processors/
│ │ │ ├── processor.py
│ │ │ ├── processor_context.py
│ │ │ └── strategies/
│ │ │ ├── __init__.py
│ │ │ ├── base_constellation_strategy.py
│ │ │ ├── constellation_creation_strategy.py
│ │ │ ├── constellation_editing_strategy.py
│ │ │ └── constellation_factory.py
│ │ ├── prompters/
│ │ │ ├── __init__.py
│ │ │ ├── base_constellation_prompter.py
│ │ │ ├── constellation_creation_prompter.py
│ │ │ └── constellation_editing_prompter.py
│ │ └── schema.py
│ ├── client/
│ │ ├── __init__.py
│ │ ├── components/
│ │ │ ├── __init__.py
│ │ │ ├── connection_manager.py
│ │ │ ├── device_registry.py
│ │ │ ├── heartbeat_manager.py
│ │ │ ├── message_processor.py
│ │ │ ├── task_queue_manager.py
│ │ │ └── types.py
│ │ ├── config_loader.py
│ │ ├── constellation_client.py
│ │ ├── demo_device_events.py
│ │ ├── device_manager.py
│ │ └── support/
│ │ ├── __init__.py
│ │ ├── client_config_manager.py
│ │ └── status_manager.py
│ ├── constellation/
│ │ ├── __init__.py
│ │ ├── editor/
│ │ │ ├── __init__.py
│ │ │ ├── command_history.py
│ │ │ ├── command_interface.py
│ │ │ ├── command_invoker.py
│ │ │ ├── command_registry.py
│ │ │ ├── commands.py
│ │ │ └── constellation_editor.py
│ │ ├── enums.py
│ │ ├── orchestrator/
│ │ │ ├── constellation_manager.py
│ │ │ └── orchestrator.py
│ │ ├── task_constellation.py
│ │ ├── task_star.py
│ │ └── task_star_line.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── di_container.py
│ │ ├── events.py
│ │ ├── interfaces.py
│ │ └── types.py
│ ├── galaxy.py
│ ├── galaxy_client.py
│ ├── prompts/
│ │ └── constellation/
│ │ ├── examples/
│ │ │ ├── constellation_creation_example.yaml
│ │ │ └── constellation_editing_example.yaml
│ │ └── share/
│ │ ├── constellation_creation.yaml
│ │ └── constellation_editing.yaml
│ ├── session/
│ │ ├── __init__.py
│ │ ├── galaxy_session.py
│ │ └── observers/
│ │ ├── __init__.py
│ │ ├── agent_output_observer.py
│ │ ├── base_observer.py
│ │ ├── constellation_sync_observer.py
│ │ ├── constellation_visualization_handler.py
│ │ ├── dag_visualization_observer.py
│ │ └── task_visualization_handler.py
│ ├── trajectory/
│ │ ├── __init__.py
│ │ ├── galaxy_parser.py
│ │ └── generate_report.py
│ ├── visualization/
│ │ ├── __init__.py
│ │ ├── change_detector.py
│ │ ├── client_display.py
│ │ ├── constellation_display.py
│ │ ├── constellation_formatter.py
│ │ ├── dag_visualizer.py
│ │ └── task_display.py
│ └── webui/
│ ├── README.md
│ ├── __init__.py
│ ├── dependencies.py
│ ├── frontend/
│ │ ├── .vite/
│ │ │ └── deps_temp_3b00ab27/
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── dist/
│ │ │ ├── assets/
│ │ │ │ ├── index-Bthiy-Xd.js
│ │ │ │ └── index-DixfhFjw.css
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── components/
│ │ │ │ ├── AgentOutput.tsx
│ │ │ │ ├── ControlPanel.tsx
│ │ │ │ ├── DAGVisualization.tsx
│ │ │ │ ├── EventLog.tsx
│ │ │ │ ├── SessionView.tsx
│ │ │ │ ├── Welcome.tsx
│ │ │ │ ├── chat/
│ │ │ │ │ ├── ChatWindow.tsx
│ │ │ │ │ ├── Composer.tsx
│ │ │ │ │ └── MessageBubble.tsx
│ │ │ │ ├── common/
│ │ │ │ │ └── SearchFilterBar.tsx
│ │ │ │ ├── constellation/
│ │ │ │ │ ├── ConstellationBlock.tsx
│ │ │ │ │ ├── ConstellationStats.tsx
│ │ │ │ │ └── DagPreview.tsx
│ │ │ │ ├── devices/
│ │ │ │ │ ├── AddDeviceModal.tsx
│ │ │ │ │ └── DevicePanel.tsx
│ │ │ │ ├── layout/
│ │ │ │ │ ├── LeftSidebar.tsx
│ │ │ │ │ ├── NotificationCenter.tsx
│ │ │ │ │ ├── RightPanel.tsx
│ │ │ │ │ └── StarfieldOverlay.tsx
│ │ │ │ ├── session/
│ │ │ │ │ └── SessionControlBar.tsx
│ │ │ │ └── tasks/
│ │ │ │ ├── TaskDetailPanel.tsx
│ │ │ │ └── TaskList.tsx
│ │ │ ├── config/
│ │ │ │ └── api.ts
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── services/
│ │ │ │ └── websocket.ts
│ │ │ ├── store/
│ │ │ │ ├── galaxyStore.ts
│ │ │ │ └── mockData.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── handlers/
│ │ ├── __init__.py
│ │ └── websocket_handlers.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── enums.py
│ │ ├── requests.py
│ │ └── responses.py
│ ├── py.typed
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── devices.py
│ │ ├── health.py
│ │ └── websocket.py
│ ├── server.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── config_service.py
│ │ ├── device_service.py
│ │ └── galaxy_service.py
│ ├── templates/
│ │ └── index.html
│ └── websocket_observer.py
├── learner/
│ ├── README.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── basic.py
│ ├── doc_example/
│ │ ├── ppt-copilot.xml
│ │ └── ppt-copilot.xml.meta
│ ├── indexer.py
│ ├── json_loader.py
│ ├── learner.py
│ ├── utils.py
│ └── xml_loader.py
├── model_worker/
│ ├── README.md
│ └── custom_worker.py
├── record_processor/
│ ├── README.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── parser/
│ │ ├── demonstration_record.py
│ │ └── psr_record_parser.py
│ ├── record_processor.py
│ ├── summarizer/
│ │ └── summarizer.py
│ └── utils/
│ └── __init__.py
├── requirements.txt
├── tests/
│ ├── BUG_REPORT_REAL_SESSION_TEST.md
│ ├── README.md
│ ├── README_log_collection_test.md
│ ├── __init__.py
│ ├── aip/
│ │ ├── __init__.py
│ │ ├── test_binary_transfer.py
│ │ ├── test_endpoints.py
│ │ ├── test_integration.py
│ │ ├── test_messages.py
│ │ ├── test_protocol.py
│ │ ├── test_resilience.py
│ │ └── test_transport.py
│ ├── bug_summary_report.py
│ ├── clients/
│ │ ├── test_comprehensive_client_types.py
│ │ ├── test_constellation_client.py
│ │ ├── test_constellation_validation.py
│ │ ├── test_device_validation.py
│ │ ├── test_handler_refactoring.py
│ │ ├── test_server_client_recognition.py
│ │ └── test_ws_client_types.py
│ ├── config/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── test_attribute_access_validation.py
│ │ ├── test_config_loader.py
│ │ ├── test_galaxy_config.py
│ │ ├── test_migration.py
│ │ ├── test_migration_validation.py
│ │ └── test_validation.py
│ ├── confirm_old_handlers_restored.py
│ ├── debug_observer_output.py
│ ├── demo_device_info.py
│ ├── demo_galaxy_client_log_collection.py
│ ├── editors/
│ │ ├── CONSTELLATION_EDITOR_UPDATES.md
│ │ ├── comprehensive_demo.py
│ │ ├── constellation_editor_example.py
│ │ ├── debug_undo.py
│ │ ├── direct_json_test.py
│ │ ├── minimal_json_test.py
│ │ ├── simple_json_test.py
│ │ ├── test_constellation_editor.py
│ │ ├── test_constellation_json.py
│ │ ├── test_constellation_mcp.py
│ │ ├── test_constellation_mcp_simplified.py
│ │ ├── test_json_serialization.py
│ │ ├── test_mcp_basic.py
│ │ ├── test_only_undo.py
│ │ └── test_updated_editor.py
│ ├── examples/
│ │ ├── __init__.py
│ │ ├── auto_id_constellation.json
│ │ ├── auto_id_example.py
│ │ ├── basemodel_example.py
│ │ ├── dict_format_example.json
│ │ ├── example_constellation.json
│ │ ├── list_dict_compatibility_example.py
│ │ └── list_format_example.json
│ ├── galaxy/
│ │ ├── __init__.py
│ │ ├── client/
│ │ │ ├── README_disconnection_tests.md
│ │ │ ├── run_disconnection_tests.py
│ │ │ ├── test_device_disconnection_reconnection.py
│ │ │ ├── test_device_disconnection_task_handling.py
│ │ │ ├── test_device_events.py
│ │ │ ├── test_device_manager_assign_task.py
│ │ │ ├── test_device_manager_info_update.py
│ │ │ ├── test_galaxy_client.py
│ │ │ ├── test_galaxy_client_cancellation.py
│ │ │ ├── test_mock_and_visualization.py
│ │ │ ├── test_mock_functionality.py
│ │ │ ├── test_pending_task_cancellation.py
│ │ │ ├── test_server_restart_reconnection.py
│ │ │ ├── test_simple_mock.py
│ │ │ ├── test_target_device_not_registered.py
│ │ │ └── test_task_response_mechanism.py
│ │ ├── constellation/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── run_all_tests.py
│ │ │ ├── test_constellation_parsing.py
│ │ │ ├── test_constellation_parsing_debug.py
│ │ │ ├── test_constellation_summary.py
│ │ │ ├── test_constellation_tasks_debug.py
│ │ │ └── test_orchestrator_cancellation.py
│ │ ├── mocks.py
│ │ ├── run_cancellation_tests.py
│ │ ├── session/
│ │ │ ├── README.md
│ │ │ ├── logs/
│ │ │ │ └── galaxy/
│ │ │ │ └── Test task analyze data and generate insights/
│ │ │ │ ├── evaluation.log
│ │ │ │ ├── request.log
│ │ │ │ └── response.log
│ │ │ ├── test_galaxy_session.py
│ │ │ ├── test_galaxy_session_final.py
│ │ │ ├── test_galaxy_session_integration.py
│ │ │ ├── test_galaxy_session_proper_mock.py
│ │ │ └── test_session_cancellation.py
│ │ ├── trajectory/
│ │ │ ├── __init__.py
│ │ │ └── test_topology_visualization.py
│ │ ├── visualization/
│ │ │ └── test_constellation_formatter.py
│ │ └── webui/
│ │ ├── test_websocket_server.py
│ │ └── test_webui_stop_integration.py
│ ├── integration/
│ │ ├── galaxy/
│ │ │ ├── test_galaxy_state_machine_integration.py
│ │ │ └── test_galaxy_state_machine_simple.py
│ │ ├── test_constellation_aip_communication.py
│ │ ├── test_constellation_aip_simple.py
│ │ ├── test_constellation_server_compatibility.py
│ │ ├── test_device_communication.py
│ │ ├── test_device_info_flow.py
│ │ ├── test_e2e_galaxy.py
│ │ ├── test_e2e_simplified.py
│ │ ├── test_galaxy_state_machine_integration.py
│ │ ├── test_mobile_mcp_server.py
│ │ ├── test_mobile_mcp_standalone.py
│ │ ├── test_presenter_integration.py
│ │ └── verify_mobile_setup.py
│ ├── logs/
│ │ └── galaxy/
│ │ └── Test task analyze data and generate insights/
│ │ ├── evaluation.log
│ │ ├── request.log
│ │ └── response.log
│ ├── run_dag_tests.py
│ ├── run_device_info_tests.py
│ ├── run_galaxy_session_tests.py
│ ├── run_galaxy_state_machine_tests.py
│ ├── run_galaxy_tests.py
│ ├── run_sync_tests.py
│ ├── test_agents_config_migration.py
│ ├── test_base_constellation_prompter.py
│ ├── test_color_fix.py
│ ├── test_constellation_continuation.py
│ ├── test_constellation_manager.py
│ ├── test_constellation_observer_logger.py
│ ├── test_constellation_parser.py
│ ├── test_constellation_parser_refactored.py
│ ├── test_constellation_serializer.py
│ ├── test_constellation_sync_integration.py
│ ├── test_constellation_sync_observer.py
│ ├── test_constellation_sync_observer_simple.py
│ ├── test_constellation_update_lock.py
│ ├── test_constellation_updater.py
│ ├── test_convert_config.py
│ ├── test_dag_visualization_observer_events.py
│ ├── test_enhanced_continuation.py
│ ├── test_galaxy_client_log_collection_session.py
│ ├── test_galaxy_framework_summary.py
│ ├── test_galaxy_session_proper_mock.py
│ ├── test_linux_log_collection_excel_generation.py
│ ├── test_logger_namespace_issue.py
│ ├── test_misc_config_migration.py
│ ├── test_old_handlers_simple.py
│ ├── test_orchestrator_refactored.py
│ ├── test_prompt_sanitizer.py
│ ├── test_race_condition_real.py
│ ├── test_real_galaxy_session_integration.py
│ ├── test_realistic_constellation_observer.py
│ ├── test_server_client_config_migration.py
│ ├── test_session_observers.py
│ ├── test_session_visualization_integration.py
│ ├── unit/
│ │ ├── galaxy/
│ │ │ ├── agents/
│ │ │ │ ├── test_constellation_factory_refactor.py
│ │ │ │ ├── test_constellation_simple.py
│ │ │ │ └── test_galaxy_agent_states.py
│ │ │ └── session/
│ │ │ ├── test_galaxy_round_refactored.py
│ │ │ ├── test_modular_observers.py
│ │ │ ├── test_observer_modular_structure.py
│ │ │ └── test_observers_refactored.py
│ │ ├── schema/
│ │ │ ├── __init__.py
│ │ │ ├── test_automatic_id_assignment.py
│ │ │ ├── test_basemodel_integration.py
│ │ │ ├── test_list_dict_compatibility.py
│ │ │ └── test_optional_fields.py
│ │ ├── test_constellation_aip_migration.py
│ │ ├── test_device_info_provider.py
│ │ ├── test_event_system.py
│ │ ├── test_galaxy_state_machine.py
│ │ ├── test_presenters.py
│ │ ├── test_refactoring.py
│ │ └── test_ws_manager_device_info.py
│ └── visualization/
│ ├── debug_constellation_modified.py
│ ├── debug_observer_output.py
│ ├── debug_visualization.py
│ ├── test_comprehensive_changes.py
│ ├── test_constellation_agent_events.py
│ ├── test_constellation_agent_integration.py
│ ├── test_constellation_comparison.py
│ ├── test_constellation_events.py
│ ├── test_dag_demo.py
│ ├── test_dag_mock.py
│ ├── test_dag_simple.py
│ ├── test_dependency_property_changes.py
│ ├── test_enhanced_visualization.py
│ ├── test_individual_events.py
│ ├── test_manual_constellation_events.py
│ └── test_refactored_modules.py
├── ufo/
│ ├── README.md
│ ├── README_UFO_V1.md
│ ├── README_ZH.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent.py
│ │ │ ├── basic.py
│ │ │ ├── customized_agent.py
│ │ │ ├── evaluation_agent.py
│ │ │ └── host_agent.py
│ │ ├── memory/
│ │ │ ├── __init__.py
│ │ │ ├── blackboard.py
│ │ │ └── memory.py
│ │ ├── presenters/
│ │ │ ├── __init__.py
│ │ │ ├── base_presenter.py
│ │ │ ├── presenter_factory.py
│ │ │ └── rich_presenter.py
│ │ ├── processors/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent_processor.py
│ │ │ ├── context/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_agent_processing_context.py
│ │ │ │ ├── host_agent_processing_context.py
│ │ │ │ └── processing_context.py
│ │ │ ├── core/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── processing_middleware.py
│ │ │ │ ├── processor_framework.py
│ │ │ │ └── strategy_dependency.py
│ │ │ ├── customized/
│ │ │ │ └── customized_agent_processor.py
│ │ │ ├── host_agent_processor.py
│ │ │ ├── schemas/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── actions.py
│ │ │ │ ├── log_schema.py
│ │ │ │ ├── response_schema.py
│ │ │ │ └── target.py
│ │ │ └── strategies/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent_processing_strategy.py
│ │ │ ├── customized_agent_processing_strategy.py
│ │ │ ├── host_agent_processing_strategy.py
│ │ │ ├── linux_agent_strategy.py
│ │ │ ├── mobile_agent_strategy.py
│ │ │ ├── processing_strategy.py
│ │ │ └── strategy_dependency.py
│ │ └── states/
│ │ ├── __init__.py
│ │ ├── app_agent_state.py
│ │ ├── basic.py
│ │ ├── evaluaton_agent_state.py
│ │ ├── host_agent_state.py
│ │ ├── linux_agent_state.py
│ │ ├── mobile_agent_state.py
│ │ └── operator_state.py
│ ├── automator/
│ │ ├── __init__.py
│ │ ├── action_execution.py
│ │ ├── app_apis/
│ │ │ ├── __init__.py
│ │ │ ├── basic.py
│ │ │ ├── excel/
│ │ │ │ ├── __init__.py
│ │ │ │ └── excelclient.py
│ │ │ ├── factory.py
│ │ │ ├── powerpoint/
│ │ │ │ ├── __init__.py
│ │ │ │ └── powerpointclient.py
│ │ │ ├── shell/
│ │ │ │ ├── __init__.py
│ │ │ │ └── shell_client.py
│ │ │ ├── web/
│ │ │ │ ├── __init__.py
│ │ │ │ └── webclient.py
│ │ │ └── word/
│ │ │ ├── __init__.py
│ │ │ └── wordclient.py
│ │ ├── basic.py
│ │ ├── puppeteer.py
│ │ └── ui_control/
│ │ ├── __init__.py
│ │ ├── control_filter.py
│ │ ├── controller.py
│ │ ├── grounding/
│ │ │ ├── __init__.py
│ │ │ ├── basic.py
│ │ │ └── omniparser.py
│ │ ├── inspector.py
│ │ ├── screenshot.py
│ │ └── ui_tree.py
│ ├── client/
│ │ ├── client.py
│ │ ├── computer.py
│ │ ├── device_info_provider.py
│ │ ├── mcp/
│ │ │ ├── http_servers/
│ │ │ │ ├── hardware_mcp_server.py
│ │ │ │ ├── linux_mcp_server.py
│ │ │ │ └── mobile_mcp_server.py
│ │ │ ├── local_servers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cli_mcp_server.py
│ │ │ │ ├── constellation_mcp_server.py
│ │ │ │ ├── excel_wincom_mcp_server.py
│ │ │ │ ├── pdf_reader_mcp_server.py
│ │ │ │ ├── ppt_wincom_mcp_server.py
│ │ │ │ ├── ui_mcp_server.py
│ │ │ │ └── word_wincom_mcp_server.py
│ │ │ ├── mcp_registry.py
│ │ │ └── mcp_server_manager.py
│ │ ├── ufo_client.py
│ │ └── websocket.py
│ ├── config/
│ │ └── __init__.py
│ ├── experience/
│ │ ├── __init__.py
│ │ ├── experience_parser.py
│ │ └── summarizer.py
│ ├── llm/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── claude.py
│ │ ├── cogagent.py
│ │ ├── config_helper.py
│ │ ├── deepseek.py
│ │ ├── gemini.py
│ │ ├── grounding_model/
│ │ │ └── omniparser_service.py
│ │ ├── llava.py
│ │ ├── llm_call.py
│ │ ├── ollama.py
│ │ ├── openai.py
│ │ ├── placeholder.py
│ │ ├── qwen.py
│ │ └── response_schema.py
│ ├── logging/
│ │ ├── __init__.py
│ │ └── setup.py
│ ├── module/
│ │ ├── __init__.py
│ │ ├── basic.py
│ │ ├── context.py
│ │ ├── dispatcher.py
│ │ ├── interactor.py
│ │ ├── session_pool.py
│ │ └── sessions/
│ │ ├── __init__.py
│ │ ├── linux_session.py
│ │ ├── mobile_session.py
│ │ ├── plan_reader.py
│ │ ├── platform_session.py
│ │ ├── service_session.py
│ │ └── session.py
│ ├── prompter/
│ │ ├── __init__.py
│ │ ├── agent_prompter.py
│ │ ├── basic.py
│ │ ├── customized/
│ │ │ ├── linux_agent_prompter.py
│ │ │ └── mobile_agent_prompter.py
│ │ ├── demonstration_prompter.py
│ │ ├── eva_prompter.py
│ │ ├── experience_prompter.py
│ │ └── prompt_sanitizer.py
│ ├── prompts/
│ │ ├── demonstration/
│ │ │ └── demonstration_summary.yaml
│ │ ├── evaluation/
│ │ │ └── evaluate.yaml
│ │ ├── examples/
│ │ │ ├── nonvisual/
│ │ │ │ ├── app_agent_example.yaml
│ │ │ │ ├── app_agent_example_as.yaml
│ │ │ │ └── host_agent_example.yaml
│ │ │ └── visual/
│ │ │ ├── app_agent_example.yaml
│ │ │ ├── app_agent_example_as.yaml
│ │ │ └── host_agent_example.yaml
│ │ ├── experience/
│ │ │ └── experience_summary.yaml
│ │ ├── share/
│ │ │ └── base/
│ │ │ ├── api.yaml
│ │ │ ├── app_agent.yaml
│ │ │ └── host_agent.yaml
│ │ └── third_party/
│ │ ├── linux_agent.yaml
│ │ ├── linux_agent_example.yaml
│ │ ├── mobile_agent.yaml
│ │ └── mobile_agent_example.yaml
│ ├── rag/
│ │ ├── __init__.py
│ │ ├── retriever.py
│ │ └── web_search.py
│ ├── server/
│ │ ├── app.py
│ │ ├── services/
│ │ │ ├── api.py
│ │ │ ├── client_connection_manager.py
│ │ │ └── session_manager.py
│ │ └── ws/
│ │ └── handler.py
│ ├── tools/
│ │ ├── README_CONFIG.md
│ │ ├── convert_config.py
│ │ ├── migrate_config.py
│ │ ├── test_config.py
│ │ └── validate_config.py
│ ├── trajectory/
│ │ └── parser.py
│ ├── ufo.py
│ └── utils/
│ └── __init__.py
└── vectordb/
└── demonstration/
└── example.yaml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/document_deploy.yml
================================================
name: Deploy MkDocs site
on:
push:
branches:
- main # 当推送到主分支时触发
- vyokky/dev # 当推送到 vyokky_dev 分支时触发
paths:
- 'documents/**' # 当 docs 目录中的文件变化时触发
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install MkDocs and dependencies
run: |
pip install mkdocs mkdocs-material mkdocstrings mkdocstrings[python]
- name: Deploy to GitHub Pages
run: |
cd documents
mkdocs gh-deploy --config-file mkdocs.yml --force
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
# Ignore login file
*.bin
# Ignore Jupyter Notebook checkpoints
.ipynb_checkpoints
/test/*
/testing/*
/deprecated/*
/test/*.ipynb
/logs/*
/customization/*
__pycache__/
**/__pycache__/
*.pyc
*.ipynb
/.VSCodeCounter
/analysis/*
/tla/*
# Ignore the config file
ufo/config/config.yaml
ufo/config/config_llm.yaml
# Ignore the helper files
ufo/rag/app_docs/*
learner/records.json
vectordb/docs/*
vectordb/experience/*
vectordb/demonstration/*
# Ignore the data files and scripts
tasks/*
scripts/*
backup/*
node_modules/*
# Don't ignore the example files
!vectordb/docs/example/
!vectordb/demonstration/example.yaml
.vscode
# Ignore the record files
tasks_status.json
datas
_datas
datasUFO
.venv/*
# Ignore mkdocs build output
documents/site/
# Ignore frontend environment files with auto-generated content
galaxy/webui/frontend/.env.development.local
# Ignore config files with sensitive data (API keys)
config/*/agents.yaml
config/*/agent.yaml
ufo/config/config.yaml
ufo/config/config_llm.yaml
# But keep config templates and default configs (except agents.yaml and agent.yaml)
!config/*/*.template
!config/*/rag.yaml
!config/*/system.yaml
!config/*/mcp.yaml
!config/*/prices.yaml
!config/*/third_party.yaml
!config/*/device.yaml
!config/*/network.yaml
node_modules
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
This project welcomes contributions and suggestions. Most contributions require you to
agree to a Contributor License Agreement (CLA) declaring that you have the right to,
and actually do, grant us the rights to use your contribution. For details, visit
https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need
to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the
instructions provided by the bot. You will only need to do this once across all repositories using our CLA.
## note
You should sunmit your pull request to the `pre-release` branch, not the `main` branch.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
================================================
FILE: DISCLAIMER.md
================================================
# Disclaimer: Code Execution and Data Handling Notice
By choosing to run the provided code, you acknowledge and agree to the following terms and conditions regarding the functionality and data handling practices:
## 1. Code Functionality:
The code you are about to execute has the capability to capture screenshots of your working desktop environment and active applications. These screenshots will be processed and sent to the GPT model for inference.
## 2. Data Privacy and Storage:
It is crucial to note that Microsoft, the provider of this code, explicitly states that it does not collect or save any of the transmitted data. The captured screenshots are processed in real-time for the purpose of inference, and no permanent storage or record of this data is retained by Microsoft.
## 3. User Responsibility:
By running the code, you understand and accept the responsibility for the content and nature of the data present on your desktop during the execution period. It is your responsibility to ensure that no sensitive or confidential information is visible or captured during this process.
## 4. Security Measures:
Microsoft has implemented security measures to safeguard the action execution. However, it is recommended that you run the code in a secure and controlled environment to minimize potential risks. Ensure that you are running the latest security updates on your system.
## 5. Consent for Inference:
You explicitly provide consent for the GPT model to analyze the captured screenshots for the purpose of generating relevant outputs. This consent is inherent in the act of executing the code.
## 6. No Guarantee of Accuracy:
The outputs generated by the GPT model are based on patterns learned during training and may not always be accurate or contextually relevant. Microsoft does not guarantee the accuracy or suitability of the inferences made by the model.
## 7. Indemnification:
Users agree to defend, indemnify, and hold Microsoft harmless from and against all damages, costs, and attorneys' fees in connection with any claims arising from the use of this Repo.
## 8. Reporting Infringements:
If anyone believes that this Repo infringes on their rights, please notify the project owner via the provided project owner email. Microsoft will investigate and take appropriate actions as necessary.
## 9. Modifications to the Disclaimer:
Microsoft reserves the right to update or modify this disclaimer at any time without prior notice. It is your responsibility to review the disclaimer periodically for any changes.
By proceeding to execute the code, you acknowledge that you have read, understood, and agreed to the terms outlined in this disclaimer. If you do not agree with these terms, refrain from running the provided code.
================================================
FILE: LICENSE
================================================
Copyright (c) Microsoft Corporation.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<!-- markdownlint-disable MD033 MD041 -->
<h1 align="center">
<b>UFO³</b> <img src="assets/logo3.png" alt="UFO logo" width="70" style="vertical-align: -30px;"> : Weaving the Digital Agent Galaxy
</h1>
<p align="center">
<em>From Single Device Agent to Multi-Device Galaxy</em>
</p>
<p align="center">
<strong>📖 Language / 语言:</strong>
<a href="README.md"><strong>English</strong></a> |
<a href="README_ZH.md">中文</a>
</p>
<div align="center">
<a href="https://trendshift.io/repositories/7874" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7874" alt="microsoft%2FUFO | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br/>
[](https://arxiv.org/abs/2511.11332) 
[](https://arxiv.org/abs/2504.14603) 
 
[](https://opensource.org/licenses/MIT) 
[](https://microsoft.github.io/UFO/) 
[](https://www.youtube.com/watch?v=NGrVWGcJL8o) 
</div>
<p align="center">
<strong>📚 Quick Links:</strong>
<a href="./galaxy/README.md">🌌 UFO³ README</a> •
<a href="./ufo/README.md">🖥️ UFO² README</a> •
<a href="https://microsoft.github.io/UFO/">📖 Full Documentation</a>
</p>
---
## 🎯 Choose Your Path
<table align="center" width="95%">
<tr>
<td width="50%" valign="top">
### <img src="assets/logo3.png" alt="Galaxy logo" width="40" style="vertical-align: -10px;"> **UFO³ Multi-Device Agent Galaxy**
<sub>**✨ NEW & RECOMMENDED**</sub>
**Perfect for:**
- 🔗 Cross-device collaboration workflows
- 📊 Complex multi-step automation
- 🎯 DAG-based task orchestration
- 🌍 Heterogeneous platform integration
**Key Features:**
- **Constellation**: Task decomposition into executable DAGs
- **Dynamic DAG editing** for adaptive workflow evolution
- **Asynchronous execution** with parallel task coordination
- **Unified AIP protocol** for secure agent communication
**📖 [Galaxy Documentation →](./galaxy/README.md)**
**📖 [Galaxy Quick Start →](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/)** ⭐ **Online Docs**
</td>
<td width="50%" valign="top">
### <img src="assets/ufo_blue.png" alt="UFO² logo" width="30" style="vertical-align: -5px;"> **UFO² Desktop AgentOS**
<sub>**STABLE & BATTLE-TESTED**</sub>
**Perfect for:**
- 💻 Single Windows automation
- ⚡ Quick task execution
- 🎓 Learning agent basics
- 🛠️ Simple workflows
**Key Features:**
- Deep Windows OS integration
- Hybrid GUI + API actions
- Proven reliability
- Easy setup
- Can serve as Galaxy device agent
**📖 [UFO² Documentation →](./ufo/README.md)**
</td>
</tr>
</table>
---
## 🎬 See UFO³ Galaxy in Action
Watch how UFO³ Galaxy orchestrates complex workflows across multiple devices:
<div align="center">
<a href="https://www.youtube.com/watch?v=NGrVWGcJL8o">
<img src="assets/poster_with_play.png" alt="UFO³ Galaxy Demo" width="90%">
</a>
<p><em>🎥 Click to watch: Cross-device task orchestration with UFO³ Galaxy</em></p>
</div>
---
## 🌟 What's New in UFO³?
### Evolution Timeline
```mermaid
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#E8F4F8','primaryTextColor':'#1A1A1A','primaryBorderColor':'#7CB9E8','lineColor':'#A8D5E2','secondaryColor':'#B8E6F0','tertiaryColor':'#D4F1F4','fontSize':'16px','fontFamily':'Segoe UI, Arial, sans-serif'}}}%%
graph LR
A["<b>🎈 UFO</b><br/><span style='font-size:14px'>February 2024</span><br/><span style='font-size:13px; color:#666'><i>GUI Agent for Windows</i></span>"]
B["<b>🖥️ UFO²</b><br/><span style='font-size:14px'>April 2025</span><br/><span style='font-size:13px; color:#666'><i>Desktop AgentOS</i></span>"]
C["<b>🌌 UFO³ Galaxy</b><br/><span style='font-size:14px'>November 2025</span><br/><span style='font-size:13px; color:#666'><i>Multi-Device Orchestration</i></span>"]
A -->|Evolve| B
B -->|Scale| C
style A fill:#E8F4F8,stroke:#7CB9E8,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
style B fill:#C5E8F5,stroke:#5BA8D0,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
style C fill:#A4DBF0,stroke:#3D96BE,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
```
### 🚀 UFO³ = **Galaxy** (Multi-Device Orchestration) + **UFO²** (Device Agent)
UFO³ introduces **Galaxy**, a revolutionary multi-device orchestration framework that coordinates intelligent agents across heterogeneous platforms. Built on five tightly integrated design principles:
1. **🌟 Declarative Decomposition into Dynamic DAG** - Requests decomposed into structured DAG with TaskStars and dependencies for automated scheduling and runtime rewriting
2. **🔄 Continuous Result-Driven Graph Evolution** - Living constellation that adapts to execution feedback through controlled rewrites and dynamic adjustments
3. **⚡ Heterogeneous, Asynchronous & Safe Orchestration** - Capability-based device matching with async execution, safe locking, and formally verified correctness
4. **🔌 Unified Agent Interaction Protocol (AIP)** - WebSocket-based secure coordination layer with fault tolerance and automatic reconnection
5. **🛠️ Template-Driven MCP-Empowered Device Agents** - Lightweight toolkit for rapid agent development with MCP integration for tool augmentation
| Aspect | UFO² | UFO³ Galaxy |
|--------|------|-------------|
| **Architecture** | Single Windows Agent | Multi-Device Orchestration |
| **Task Model** | Sequential ReAct Loop | DAG-based Constellation Workflows |
| **Scope** | Single device, multi-app | Multi-device, cross-platform |
| **Coordination** | HostAgent + AppAgents | ConstellationAgent + TaskOrchestrator |
| **Device Support** | Windows Desktop | Windows, Linux, Android (more coming) |
| **Task Planning** | Application-level | Device-level with dependencies |
| **Execution** | Sequential | Parallel DAG execution |
| **Device Agent Role** | Standalone | Can serve as Galaxy device agent |
| **Complexity** | Simple to Moderate | Simple to Very Complex |
| **Learning Curve** | Low | Moderate |
| **Cross-Device Collaboration** | ❌ Not Supported | ✅ Core Feature |
| **Setup Difficulty** | ✅ Easy | ⚠️ Moderate |
| **Status** | ✅ LTS (Long-Term Support) | ⚡ Active Development |
### 🎓 Migration Path
**For UFO² Users:**
1. ✅ **Keep using UFO²** – Fully supported, actively maintained
2. 🔄 **Gradual adoption** – Galaxy can use UFO² as Windows device agent
3. 📈 **Scale up** – Move to Galaxy when you need multi-device capabilities
4. 📚 **Learning resources** – [Migration Guide](./documents/docs/getting_started/migration_ufo2_to_galaxy.md)
---
## ✨ Capabilities at a Glance
### 🌌 Galaxy Framework – What's Different?
<table>
<tr>
<td width="33%" valign="top">
#### 🌟 Constellation Planning
```
User Request
↓
ConstellationAgent
↓
[Task DAG]
/ | \
Task1 Task2 Task3
(Win) (Linux)(Mac)
```
**Benefits:**
- Cross-device dependency tracking
- Parallel execution optimization
- Cross-device dataflow management
</td>
<td width="33%" valign="top">
#### 🎯 Device Assignment
```
Selection Criteria
• Platform
• Resource
• Task requirements
• Performance history
↓
Auto-Assignment
↓
Optimal Devices
```
**Smart Matching:**
- Capability-based selection
- Real-time resource monitoring
- Dynamic reallocation
</td>
<td width="33%" valign="top">
#### 📊 Orchestration
```
Task1 → Running ✅
Task2 → Pending ⏸️
Task3 → Running 🔄
↓
Completion
↓
Final Report
```
**Orchestration:**
- Real-time status updates
- Automatic error recovery
- Progress tracking with feedback
</td>
</tr>
</table>
---
### 🪟 UFO² Desktop AgentOS – Core Strengths
UFO² serves dual roles: **standalone Windows automation** and **Galaxy device agent** for Windows platforms.
<div align="center">
| Feature | Description | Documentation |
|---------|-------------|---------------|
| **Deep OS Integration** | Windows UIA, Win32, WinCOM native control | [Learn More](https://microsoft.github.io/UFO) |
| **Hybrid Actions** | GUI clicks + API calls for optimal performance | [Learn More](https://microsoft.github.io/UFO/automator/overview) |
| **Speculative Multi-Action** | Batch predictions → **51% fewer LLM calls** | [Learn More](https://microsoft.github.io/UFO/advanced_usage/multi_action) |
| **Visual + UIA Detection** | Hybrid control detection for robustness | [Learn More](https://microsoft.github.io/UFO/advanced_usage/control_detection/hybrid_detection) |
| **Knowledge Substrate** | RAG with docs, demos, execution traces | [Learn More](https://microsoft.github.io/UFO/advanced_usage/reinforce_appagent/overview/) |
| **Device Agent Role** | Can serve as Windows executor in Galaxy orchestration | [Learn More](./galaxy/README.md) |
</div>
**As Galaxy Device Agent:**
- Receives tasks from ConstellationAgent via Galaxy orchestration layer
- Executes Windows-specific operations using proven UFO² capabilities
- Reports status and results back to TaskOrchestrator
- Participates in cross-device workflows seamlessly
---
## 🚀 Quick Start Guide
Choose your path and follow the detailed setup guide:
<table align="center">
<tr>
<td width="50%" valign="top">
### 🌌 Galaxy Quick Start
**For cross-device orchestration**
```powershell
# 1. Install
pip install -r requirements.txt
# 2. Configure ConstellationAgent
copy config\galaxy\agent.yaml.template config\galaxy\agent.yaml
# Edit and add your API keys
# 3. Configure devices
# Edit config\galaxy\devices.yaml to register your devices
# 4. Start device agents (with platform flags)
# Windows: Start server + client
# Linux: Start server + MCP servers + client
# Mobile (Android): Start server + MCP servers + client
# See platform-specific guides for detailed setup
# 5. Launch Galaxy
python -m galaxy --interactive
```
**📖 Complete Guide:**
- [Galaxy README](./galaxy/README.md) – Architecture & concepts
- [Online Quick Start](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/) – Step-by-step tutorial
- [Windows Device Setup](https://microsoft.github.io/UFO/getting_started/quick_start_ufo2/)
- [Linux Device Setup](https://microsoft.github.io/UFO/getting_started/quick_start_linux/)
- [Mobile Device Setup](https://microsoft.github.io/UFO/getting_started/quick_start_mobile/) – Android agent setup
- [Configuration](https://microsoft.github.io/UFO/configuration/system/galaxy_devices/) – Device pool configuration
</td>
<td width="50%" valign="top">
### 🪟 UFO² Quick Start
**For Windows automation**
```powershell
# 1. Install
pip install -r requirements.txt
# 2. Configure
copy config\ufo\agents.yaml.template config\ufo\agents.yaml
# Edit and add your API keys
# 3. Run
python -m ufo --task <task_name>
```
**📖 Complete Guide:**
- [UFO² README](./ufo/README.md) – Full documentation
- [Configuration Guide](./ufo/README.md#️-step-2-configure-the-llms) – LLM setup
- [Advanced Features](https://microsoft.github.io/UFO/advanced_usage/overview/) – Multi-action, RAG
</td>
</tr>
</table>
### 📋 Common Configuration
Both frameworks require LLM API configuration. Choose your provider:
<details>
<summary><strong>OpenAI Configuration</strong></summary>
**For Galaxy (`config/galaxy/agent.yaml`):**
```yaml
CONSTELLATION_AGENT:
REASONING_MODEL: false
API_TYPE: "openai"
API_BASE: "https://api.openai.com/v1/chat/completions"
API_KEY: "sk-your-key-here"
API_MODEL: "gpt-4o"
```
**For UFO² (`config/ufo/agents.yaml`):**
```yaml
VISUAL_MODE: True
API_TYPE: "openai"
API_BASE: "https://api.openai.com/v1/chat/completions"
API_KEY: "sk-your-key-here"
API_MODEL: "gpt-4o"
```
</details>
<details>
<summary><strong>Azure OpenAI Configuration</strong></summary>
**For Galaxy (`config/galaxy/agent.yaml`):**
```yaml
CONSTELLATION_AGENT:
REASONING_MODEL: false
API_TYPE: "aoai"
API_BASE: "https://YOUR-RESOURCE.openai.azure.com"
API_KEY: "your-azure-key"
API_MODEL: "gpt-4o"
API_DEPLOYMENT_ID: "your-deployment-id"
```
**For UFO² (`config/ufo/agents.yaml`):**
```yaml
VISUAL_MODE: True
API_TYPE: "aoai"
API_BASE: "https://YOUR-RESOURCE.openai.azure.com"
API_KEY: "your-azure-key"
API_MODEL: "gpt-4o"
API_DEPLOYMENT_ID: "your-deployment-id"
```
</details>
> 💡 **More LLM Options:** See [Model Configuration Guide](https://microsoft.github.io/UFO/supported_models/overview/) for Qwen, Gemini, Claude, and more.
---
## 📚 Documentation Structure
<table>
<tr>
<td width="50%" valign="top">
### 🌌 Galaxy Documentation
- **[Galaxy Framework Overview](./galaxy/README.md)** ⭐ **Start Here** – Architecture & technical concepts
- **[Quick Start Tutorial](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/)** – Get running in minutes
- **[Galaxy Client](https://microsoft.github.io/UFO/galaxy/client/overview/)** – Device coordination and API
- **[Constellation Agent](https://microsoft.github.io/UFO/galaxy/constellation_agent/overview/)** – Task decomposition and planning
- **[Task Orchestrator](https://microsoft.github.io/UFO/galaxy/constellation_orchestrator/overview/)** – Execution engine
- **[Task Constellation](https://microsoft.github.io/UFO/galaxy/constellation/overview/)** – DAG structure
- **[Agent Registration](https://microsoft.github.io/UFO/galaxy/agent_registration/overview/)** – Device registry
- **[Configuration Guide](https://microsoft.github.io/UFO/configuration/system/galaxy_devices/)** – Setup and device pools
**📖 Technical Documentation:**
- [AIP Protocol](https://microsoft.github.io/UFO/aip/overview/) – WebSocket messaging
- [Session Management](https://microsoft.github.io/UFO/galaxy/session/overview/) – Session lifecycle
- [Visualization](https://microsoft.github.io/UFO/galaxy/visualization/overview/) – Real-time monitoring
- [Events & Observers](https://microsoft.github.io/UFO/galaxy/core/overview/) – Event system
</td>
<td width="50%" valign="top">
### 🪟 UFO² Documentation
- **[UFO² Overview](./ufo/README.md)** – Desktop AgentOS architecture
- **[Installation](./ufo/README.md#️-step-1-installation)** – Setup & dependencies
- **[Configuration](./ufo/README.md#️-step-2-configure-the-llms)** – LLM & RAG setup
- **[Usage Guide](./ufo/README.md#-step-4-start-ufo)** – Running UFO²
- **[Advanced Features](https://microsoft.github.io/UFO/advanced_usage/overview/)** – Multi-action, RAG, etc.
- **[Automator Guide](https://microsoft.github.io/UFO/automator/overview)** – Hybrid GUI + API
- **[Benchmarks](./ufo/README.md#-evaluation)** – WAA & OSWorld results
**📖 Online Docs:**
- [Complete Documentation](https://microsoft.github.io/UFO/)
- [Model Support](https://microsoft.github.io/UFO/supported_models/overview/)
- [RAG Configuration](https://microsoft.github.io/UFO/advanced_usage/reinforce_appagent/overview/)
</td>
</tr>
</table>
---
## 📢 Latest Updates
### 2025-11 – UFO³ Galaxy Framework Released 🌌
**Major Research Breakthrough:** Multi-Device Orchestration System
- 🌟 **Declarative DAG Decomposition**: TaskConstellation structure for workflow logic and dependencies
- 🔄 **Dynamic Graph Evolution**: Living constellation that adapts through controlled rewrites
- 🎯 **Heterogeneous Orchestration**: Safe, asynchronous execution with capability-based device matching
- 🔌 **Unified AIP Protocol**: WebSocket-based secure agent coordination with fault tolerance
- 🛠️ **MCP-Empowered Agent Framework**: Template-driven toolkit for rapid device agent development
- 📄 **Research Paper**: [UFO³: Weaving the Digital Agent Galaxy](https://arxiv.org/abs/2511.11332)
**Key Features:**
- First multi-device orchestration framework for GUI agents
- Result-driven adaptive execution instead of rigid workflows
- Model Context Protocol (MCP) integration for tool augmentation
- Formally verified correctness and concurrency safety guarantees
### 2025-04 – UFO² v2.0.0
- 📅 UFO² Desktop AgentOS released
- 🏗️ Enhanced architecture with AgentOS concept
- 📄 [Technical Report](https://arxiv.org/pdf/2504.14603) published
- ✅ Entered Long-Term Support (LTS) status
### 2024-02 – Original UFO
- 🎈 First UFO release - UI-Focused agent for Windows
- 📄 [Original Paper](https://arxiv.org/abs/2402.07939)
- 🌍 Wide media coverage and adoption
---
## 📚 Citation
If you use UFO³ Galaxy or UFO² in your research, please cite the relevant papers:
### UFO³ Galaxy Framework (2025)
```bibtex
@article{zhang2025ufo3,
title={UFO$^3$: Weaving the Digital Agent Galaxy},
author = {Zhang, Chaoyun and Li, Liqun and Huang, He and Ni, Chiming and Qiao, Bo and Qin, Si and Kang, Yu and Ma, Minghua and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei},
journal = {arXiv preprint arXiv:2511.11332},
year = {2025},
}
```
### UFO² Desktop AgentOS (2025)
```bibtex
@article{zhang2025ufo2,
title = {{UFO2: The Desktop AgentOS}},
author = {Zhang, Chaoyun and Huang, He and Ni, Chiming and Mu, Jian and Qin, Si and He, Shilin and Wang, Lu and Yang, Fangkai and Zhao, Pu and Du, Chao and Li, Liqun and Kang, Yu and Jiang, Zhao and Zheng, Suzhen and Wang, Rujia and Qian, Jiaxu and Ma, Minghua and Lou, Jian-Guang and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei},
journal = {arXiv preprint arXiv:2504.14603},
year = {2025}
}
```
### Original UFO (2024)
```bibtex
@article{zhang2024ufo,
title = {{UFO: A UI-Focused Agent for Windows OS Interaction}},
author = {Zhang, Chaoyun and Li, Liqun and He, Shilin and Zhang, Xu and Qiao, Bo and Qin, Si and Ma, Minghua and Kang, Yu and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei and Zhang, Qi},
journal = {arXiv preprint arXiv:2402.07939},
year = {2024}
}
```
---
## 🌐 Media & Community
**Media Coverage:**
- [微软正式开源UFO²,Windows桌面迈入「AgentOS 时代」](https://www.jiqizhixin.com/articles/2025-05-06-13)
- [Microsoft's UFO: Smarter Windows Experience](https://the-decoder.com/microsofts-ufo-abducts-traditional-user-interfaces-for-a-smarter-windows-experience/)
- [下一代Windows系统曝光](https://baijiahao.baidu.com/s?id=1790938358152188625)
- **[More coverage →](./ufo/README.md#-tracing-the-stars)**
**Community:**
- 💬 [GitHub Discussions](https://github.com/microsoft/UFO/discussions)
- 🐛 [Issue Tracker](https://github.com/microsoft/UFO/issues)
- 📧 Email: [ufo-agent@microsoft.com](mailto:ufo-agent@microsoft.com)
- 📺 [YouTube Channel](https://www.youtube.com/watch?v=QT_OhygMVXU)
---
## 🎨 Related Projects & Research
**Microsoft Research:**
- **[TaskWeaver](https://github.com/microsoft/TaskWeaver)** – Code-first LLM agent framework for data analytics and task automation
**GUI Agent Research:**
- **[LLM-Brained GUI Agents Survey](https://github.com/vyokky/LLM-Brained-GUI-Agents-Survey)** – Comprehensive survey of GUI automation agents
- **[Interactive Survey Site](https://vyokky.github.io/LLM-Brained-GUI-Agents-Survey/)** – Explore latest GUI agent research and developments
**Multi-Agent Systems:**
- **UFO³ Galaxy** represents a novel approach to multi-device orchestration, introducing the Constellation framework for coordinating heterogeneous agents across platforms
- Builds on multi-agent coordination research while addressing unique challenges of cross-device GUI automation
**Benchmarks:**
- **[Windows Agent Arena (WAA)](https://github.com/nice-mee/WindowsAgentArena)** – Evaluation benchmark for Windows automation agents
- **[OSWorld](https://github.com/nice-mee/WindowsAgentArena/tree/2020-qqtcg/osworld)** – Cross-application task evaluation suite
---
## 💡 FAQ
<details>
<summary><strong>🤔 Should I use Galaxy or UFO²?</strong></summary>
**Start with UFO²** if:
- You only need Windows automation
- You want quick setup and learning
- Tasks are relatively simple
**Choose Galaxy** if:
- You need cross-device coordination
- Tasks are complex and multi-step
- You want advanced orchestration
- You're comfortable with active development
**Hybrid approach** if:
- You want best of both worlds
- Some tasks are simple (UFO²), some complex (Galaxy)
- You're gradually migrating
</details>
<details>
<summary><strong>⚠️ Will UFO² be deprecated?</strong></summary>
**No!** UFO² has entered **Long-Term Support (LTS)** status:
- ✅ Actively maintained
- ✅ Bug fixes and security updates
- ✅ Performance improvements
- ✅ Full community support
- ✅ No plans for deprecation
UFO² is the stable, proven solution for Windows automation.
</details>
<details>
<summary><strong>🔄 How do I migrate from UFO² to Galaxy?</strong></summary>
Migration is **gradual and optional**:
1. **Phase 1: Learn** – Understand Galaxy concepts
2. **Phase 2: Experiment** – Try Galaxy with non-critical tasks
3. **Phase 3: Hybrid** – Use both frameworks
4. **Phase 4: Migrate** – Gradually move complex tasks to Galaxy
**No forced migration!** Continue using UFO² as long as it meets your needs.
See [Migration Guide](./documents/docs/getting_started/migration_ufo2_to_galaxy.md) for details.
</details>
<details>
<summary><strong>🎯 Can Galaxy do everything UFO² does?</strong></summary>
**Functionally: Yes.** Galaxy can use UFO² as a Windows device agent.
**Practically: It depends.**
- For **simple Windows tasks**: UFO² standalone is easier and more streamlined
- For **complex workflows**: Galaxy orchestrates UFO² with other device agents
**Recommendation:** Use the right tool for the job. UFO² can work standalone or as Galaxy's Windows device agent.
</details>
<details>
<summary><strong>📊 How mature is Galaxy?</strong></summary>
**Status: Active Development** 🚧
**Stable:**
- ✅ Core architecture
- ✅ DAG orchestration
- ✅ Basic multi-device support
- ✅ Event system
**In Development:**
- 🔨 Advanced device types
- 🔨 Enhanced monitoring
- 🔨 Performance optimization
- 🔨 Extended documentation
**Recommendation:** Great for experimentation and non-critical workflows.
</details>
<details>
<summary><strong>🔧 Can I extend or customize?</strong></summary>
**Both frameworks are highly extensible:**
**UFO²:**
- Custom actions and automators
- Custom knowledge sources (RAG)
- Custom control detectors
- Custom evaluation metrics
**Galaxy:**
- Custom agents
- Custom device types
- Custom orchestration strategies
- Custom visualization components
See respective documentation for extension guides.
</details>
<details>
<summary><strong>🤝 How can I contribute?</strong></summary>
We welcome contributions to both UFO² and Galaxy!
**Ways to contribute:**
- 🐛 Report bugs and issues
- 💡 Suggest features and improvements
- 📝 Improve documentation
- 🧪 Add tests and examples
- 🔧 Submit pull requests
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
</details>
---
## ⚠️ Disclaimer & License
**Disclaimer:** By using this software, you acknowledge and agree to the terms in [DISCLAIMER.md](./DISCLAIMER.md).
**License:** This project is licensed under the [MIT License](LICENSE).
**Trademarks:** Use of Microsoft trademarks follows [Microsoft's Trademark Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
---
<div align="center">
## 🚀 Ready to Get Started?
<table>
<tr>
<td align="center" width="50%">
### 🌌 Explore Galaxy
**Multi-Device Orchestration**
[](./galaxy/README.md)
</td>
<td align="center" width="50%">
### 🪟 Try UFO²
**Windows Desktop Agent**
[](./ufo/README.md)
</td>
</tr>
</table>
---
<sub>© Microsoft 2025 | UFO³ is an open-source research project</sub>
<sub>⭐ Star us on GitHub | 🤝 Contribute | 📖 Read the docs | 💬 Join discussions</sub>
</div>
---
<p align="center">
<img src="assets/logo3.png" alt="UFO logo" width="60">
<br>
<em>From Single Agent to Digital Galaxy</em>
<br>
<strong>UFO³ - Weaving the Future of Intelligent Automation</strong>
</p>
================================================
FILE: README_ZH.md
================================================
<!-- markdownlint-disable MD033 MD041 -->
<h1 align="center">
<b>UFO³</b> <img src="assets/logo3.png" alt="UFO logo" width="70" style="vertical-align: -30px;"> : 编织数字智能体星系
</h1>
<p align="center">
<em>从单设备智能体到多设备星系</em>
</p>
<p align="center">
<strong>📖 Language / 语言:</strong>
<a href="README.md">English</a> |
<strong>中文</strong>
</p>
<div align="center">
<a href="https://trendshift.io/repositories/7874" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7874" alt="microsoft%2FUFO | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br/>
[](https://arxiv.org/abs/2511.11332) 
[](https://arxiv.org/abs/2504.14603) 
 
[](https://opensource.org/licenses/MIT) 
[](https://microsoft.github.io/UFO/) 
[](https://www.youtube.com/watch?v=NGrVWGcJL8o) 
</div>
<p align="center">
<strong>📚 快速链接:</strong>
<a href="./galaxy/README_ZH.md">🌌 UFO³ 中文文档</a> •
<a href="./ufo/README_ZH.md">🖥️ UFO² 中文文档</a> •
<a href="https://microsoft.github.io/UFO/">📖 完整文档</a>
</p>
---
## 🎯 选择您的路径
<table align="center">
<tr>
<td width="50%" valign="top">
### <img src="assets/logo3.png" alt="Galaxy logo" width="40" style="vertical-align: -10px;"> **UFO³ 多设备智能体星系**
<sub>**✨ 新功能 & 推荐**</sub>
**适用于:**
- 🔗 跨设备协作工作流
- 📊 复杂的多步骤自动化
- 🎯 基于 DAG 的任务编排
- 🌍 异构平台集成
**关键功能:**
- **星座(Constellation)**:任务分解为可执行 DAG
- **动态 DAG 编辑**,自适应工作流演化
- **异步执行**,并行任务协调
- **统一 AIP 协议**,安全智能体通信
**📖 [Galaxy 中文文档 →](./galaxy/README_ZH.md)**
**📖 [Galaxy 快速入门 →](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/)** ⭐ **在线文档**
</td>
<td width="50%" valign="top">
### <img src="assets/ufo_blue.png" alt="UFO² logo" width="30" style="vertical-align: -5px;"> **UFO² 桌面智能体操作系统**
<sub>**稳定 & 经过实战检验**</sub>
**适用于:**
- 💻 单个 Windows 自动化
- ⚡ 快速任务执行
- 🎓 学习智能体基础知识
- 🛠️ 简单工作流
**关键功能:**
- 深度 Windows 操作系统集成
- 混合 GUI + API 操作
- 经过验证的可靠性
- 易于设置
- 可作为 Galaxy 设备智能体
**📖 [UFO² 中文文档 →](./ufo/README_ZH.md)**
</td>
</tr>
</table>
---
## 🎬 观看 UFO³ Galaxy 实际操作
观看 UFO³ Galaxy 如何跨多个设备编排复杂工作流:
<div align="center">
<a href="https://www.youtube.com/watch?v=NGrVWGcJL8o">
<img src="assets/poster_with_play.png" alt="UFO³ Galaxy 演示" width="90%">
</a>
<p><em>🎥 点击观看:使用 UFO³ Galaxy 进行跨设备任务编排</em></p>
</div>
---
## 🌟 UFO³ 有什么新功能?
### 演化时间线
```mermaid
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#E8F4F8','primaryTextColor':'#1A1A1A','primaryBorderColor':'#7CB9E8','lineColor':'#A8D5E2','secondaryColor':'#B8E6F0','tertiaryColor':'#D4F1F4','fontSize':'16px','fontFamily':'Microsoft YaHei, Segoe UI, Arial, sans-serif'}}}%%
graph LR
A["<b>🎈 UFO</b><br/><span style='font-size:14px'>2024年2月</span><br/><span style='font-size:13px; color:#666'><i>Windows GUI 智能体</i></span>"]
B["<b>🖥️ UFO²</b><br/><span style='font-size:14px'>2025年4月</span><br/><span style='font-size:13px; color:#666'><i>桌面智能体操作系统</i></span>"]
C["<b>🌌 UFO³ Galaxy</b><br/><span style='font-size:14px'>2025年11月</span><br/><span style='font-size:13px; color:#666'><i>多设备编排</i></span>"]
A -->|演进| B
B -->|扩展| C
style A fill:#E8F4F8,stroke:#7CB9E8,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
style B fill:#C5E8F5,stroke:#5BA8D0,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
style C fill:#A4DBF0,stroke:#3D96BE,stroke-width:2.5px,color:#1A1A1A,rx:15,ry:15
```
### 🚀 UFO³ = **Galaxy**(多设备编排)+ **UFO²**(设备智能体)
UFO³ 引入了 **Galaxy**,这是一个革命性的多设备编排框架,可在异构平台上协调智能智能体。建立在五个紧密集成的设计原则之上:
1. **🌟 声明式分解为动态 DAG** - 请求分解为带有 TaskStars 和依赖关系的结构化 DAG,支持自动调度和运行时重写
2. **🔄 持续的结果驱动图演化** - 活动星座根据执行反馈通过受控重写和动态调整进行适应
3. **⚡ 异构、异步与安全编排** - 基于能力的设备匹配,异步执行、安全锁定和形式化验证的正确性
4. **🔌 统一的智能体交互协议(AIP)** - 基于 WebSocket 的安全协调层,具有容错和自动重连功能
5. **🛠️ 模板驱动的 MCP 赋能设备智能体** - 用于快速智能体开发的轻量级工具包,集成 MCP 进行工具增强
| 方面 | UFO² | UFO³ Galaxy |
|--------|------|-------------|
| **架构** | 单个 Windows 智能体 | 多设备编排 |
| **任务模型** | 顺序 ReAct 循环 | 基于 DAG 的星座工作流 |
| **范围** | 单设备,多应用 | 多设备,跨平台 |
| **协调** | HostAgent + AppAgents | ConstellationAgent + TaskOrchestrator |
| **设备支持** | Windows 桌面 | Windows、Linux、Android(更多平台即将推出) |
| **任务规划** | 应用程序级别 | 设备级别,带依赖关系 |
| **执行** | 顺序 | 并行 DAG 执行 |
| **设备智能体角色** | 独立 | 可作为 Galaxy 设备智能体 |
| **复杂性** | 简单到中等 | 简单到非常复杂 |
| **学习曲线** | 低 | 中等 |
| **跨设备协作** | ❌ 不支持 | ✅ 核心功能 |
| **设置难度** | ✅ 简单 | ⚠️ 中等 |
| **状态** | ✅ LTS(长期支持) | ⚡ 积极开发 |
### 🎓 迁移路径
**对于 UFO² 用户:**
1. ✅ **继续使用 UFO²** – 完全支持,积极维护
2. 🔄 **渐进式采用** – Galaxy 可以使用 UFO² 作为 Windows 设备智能体
3. 📈 **扩展** – 当您需要多设备功能时迁移到 Galaxy
4. 📚 **学习资源** – [迁移指南](./documents/docs/getting_started/migration_ufo2_to_galaxy.md)
---
## ✨ 功能概览
### 🌌 Galaxy 框架 – 有什么不同?
<table>
<tr>
<td width="33%" valign="top">
#### 🌟 星座规划
```
用户请求
↓
星座智能体
↓
[任务 DAG]
/ | \
任务1 任务2 任务3
(Win) (Linux)(Mac)
```
**优势:**
- 跨设备依赖关系跟踪
- 并行执行优化
- 跨设备数据流管理
</td>
<td width="33%" valign="top">
#### 🎯 设备分配
```
选择标准
• 平台兼容性
• 资源可用性
• 任务要求
• 性能历史
↓
自动分配
↓
最佳设备
```
**智能匹配:**
- 基于能力的选择
- 实时资源监控
- 动态重新分配
</td>
<td width="33%" valign="top">
#### 📊 实时编排
```
任务1 → 运行中 ✅
任务2 → 等待中 ⏸️
任务3 → 运行中 🔄
↓
完成汇总
↓
最终报告
```
**编排功能:**
- 实时状态更新
- 自动错误恢复
- 进度跟踪反馈
</td>
</tr>
</table>
---
### 🪟 UFO² 桌面智能体操作系统 – 核心优势
UFO² 扮演双重角色:**独立 Windows 自动化**和 Windows 平台的 **Galaxy 设备智能体**。
<div align="center">
| 功能 | 描述 | 文档 |
|---------|-------------|---------------|
| **深度操作系统集成** | Windows UIA、Win32、WinCOM 原生控件 | [了解更多](https://microsoft.github.io/UFO) |
| **混合操作** | GUI 点击 + API 调用以获得最佳性能 | [了解更多](https://microsoft.github.io/UFO/automator/overview) |
| **推测性多操作** | 批量预测 → **减少 51% 的 LLM 调用** | [了解更多](https://microsoft.github.io/UFO/advanced_usage/multi_action) |
| **视觉 + UIA 检测** | 用于稳健性的混合控件检测 | [了解更多](https://microsoft.github.io/UFO/advanced_usage/control_detection/hybrid_detection) |
| **知识基底** | 带有文档、演示、执行轨迹的 RAG | [了解更多](https://microsoft.github.io/UFO/advanced_usage/reinforce_appagent/overview/) |
| **设备智能体角色** | 可作为 Galaxy 编排中的 Windows 执行器 | [了解更多](./galaxy/README_ZH.md) |
</div>
**作为 Galaxy 设备智能体:**
- 通过 Galaxy 编排层从 ConstellationAgent 接收任务
- 使用经过验证的 UFO² 功能执行 Windows 特定的操作
- 向 TaskOrchestrator 报告状态和结果
- 无缝参与跨设备工作流
---
## 🚀 快速入门指南
选择您的路径并遵循详细的设置指南:
<table align="center">
<tr>
<td width="50%" valign="top">
### 🌌 Galaxy 快速入门
**用于跨设备编排**
```powershell
# 1. 安装依赖
pip install -r requirements.txt
# 2. 配置 ConstellationAgent
copy config\galaxy\agent.yaml.template config\galaxy\agent.yaml
# 编辑配置文件,添加 API Key
# 3. 配置设备
# 编辑 config\galaxy\devices.yaml 注册您的设备
# 4. 启动设备智能体(带平台标志)
# Windows: 启动服务器 + 客户端
# Linux: 启动服务器 + MCP 服务器 + 客户端
# Mobile (Android): 启动服务器 + MCP 服务器 + 客户端
# 请参阅特定平台指南了解详细设置
# 5. 启动 Galaxy
python -m galaxy --interactive
```
**📖 完整指南:**
- [Galaxy 中文文档](./galaxy/README_ZH.md) – 架构和概念
- [在线快速入门](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/) – 分步教程
- [Windows 设备设置](https://microsoft.github.io/UFO/getting_started/quick_start_ufo2/)
- [Linux 设备设置](https://microsoft.github.io/UFO/getting_started/quick_start_linux/)
- [Mobile 设备设置](https://microsoft.github.io/UFO/getting_started/quick_start_mobile/) – Android 智能体设置
- [配置](https://microsoft.github.io/UFO/configuration/system/galaxy_devices/) – 设备池配置
</td>
<td width="50%" valign="top">
### 🪟 UFO² 快速入门
**用于 Windows 自动化**
```powershell
# 1. 安装
pip install -r requirements.txt
# 2. 配置
copy config\ufo\agents.yaml.template config\ufo\agents.yaml
# 编辑并添加您的 API 密钥
# 3. 运行
python -m ufo --task <task_name>
```
**📖 完整指南:**
- [UFO² 中文文档](./ufo/README_ZH.md) – 完整文档
- [配置指南](./ufo/README_ZH.md#️-步骤-2配置-llm) – LLM 设置
- [高级功能](https://microsoft.github.io/UFO/advanced_usage/overview/) – 多操作、RAG
</td>
</tr>
</table>
### 📋 常见配置
两个框架都需要 LLM API 配置。选择您的提供商:
<details>
<summary><strong>OpenAI 配置</strong></summary>
**对于 Galaxy (`config/galaxy/agent.yaml`):**
```yaml
CONSTELLATION_AGENT:
REASONING_MODEL: false
API_TYPE: "openai"
API_BASE: "https://api.openai.com/v1/chat/completions"
API_KEY: "sk-your-key-here"
API_MODEL: "gpt-4o"
```
**对于 UFO² (`config/ufo/agents.yaml`):**
```yaml
VISUAL_MODE: True
API_TYPE: "openai"
API_BASE: "https://api.openai.com/v1/chat/completions"
API_KEY: "sk-your-key-here"
API_MODEL: "gpt-4o"
```
</details>
<details>
<summary><strong>Azure OpenAI 配置</strong></summary>
**对于 Galaxy (`config/galaxy/agent.yaml`):**
```yaml
CONSTELLATION_AGENT:
REASONING_MODEL: false
API_TYPE: "aoai"
API_BASE: "https://YOUR-RESOURCE.openai.azure.com"
API_KEY: "your-azure-key"
API_MODEL: "gpt-4o"
API_DEPLOYMENT_ID: "your-deployment-id"
```
**对于 UFO² (`config/ufo/agents.yaml`):**
```yaml
VISUAL_MODE: True
API_TYPE: "aoai"
API_BASE: "https://YOUR-RESOURCE.openai.azure.com"
API_KEY: "your-azure-key"
API_MODEL: "gpt-4o"
API_DEPLOYMENT_ID: "your-deployment-id"
```
</details>
> 💡 **更多 LLM 选项:** 有关 Qwen、Gemini、Claude 等的信息,请参阅[模型配置指南](https://microsoft.github.io/UFO/supported_models/overview/)。
---
## 📚 文档结构
<table>
<tr>
<td width="50%" valign="top">
### 🌌 Galaxy 文档
- **[Galaxy 框架概述](./galaxy/README_ZH.md)** ⭐ **从这里开始** – 架构和技术概念
- **[快速入门教程](https://microsoft.github.io/UFO/getting_started/quick_start_galaxy/)** – 几分钟内开始运行
- **[Galaxy 客户端](https://microsoft.github.io/UFO/galaxy/client/overview/)** – 设备协调和 API
- **[星座智能体](https://microsoft.github.io/UFO/galaxy/constellation_agent/overview/)** – 任务分解和规划
- **[任务编排器](https://microsoft.github.io/UFO/galaxy/constellation_orchestrator/overview/)** – 执行引擎
- **[任务星座](https://microsoft.github.io/UFO/galaxy/constellation/overview/)** – DAG 结构
- **[智能体注册](https://microsoft.github.io/UFO/galaxy/agent_registration/overview/)** – 设备注册表
- **[配置指南](https://microsoft.github.io/UFO/configuration/system/galaxy_devices/)** – 设置和设备池
**📖 技术文档:**
- [AIP 协议](https://microsoft.github.io/UFO/aip/overview/) – WebSocket 消息传递
- [会话管理](https://microsoft.github.io/UFO/galaxy/session/overview/) – 会话生命周期
- [可视化](https://microsoft.github.io/UFO/galaxy/visualization/overview/) – 实时监控
- [事件和观察者](https://microsoft.github.io/UFO/galaxy/core/overview/) – 事件系统
</td>
<td width="50%" valign="top">
### 🪟 UFO² 文档
- **[UFO² 概述](./ufo/README_ZH.md)** – 桌面智能体操作系统架构
- **[安装](./ufo/README_ZH.md#️-步骤-1安装)** – 设置和依赖
- **[配置](./ufo/README_ZH.md#️-步骤-2配置-llm)** – LLM 和 RAG 设置
- **[使用指南](./ufo/README_ZH.md#-步骤-4启动-ufo)** – 运行 UFO²
- **[高级功能](https://microsoft.github.io/UFO/advanced_usage/overview/)** – 多操作、RAG 等
- **[自动化器指南](https://microsoft.github.io/UFO/automator/overview)** – 混合 GUI + API
- **[基准测试](./ufo/README_ZH.md#-评估)** – WAA 和 OSWorld 结果
**📖 在线文档:**
- [完整文档](https://microsoft.github.io/UFO/)
- [模型支持](https://microsoft.github.io/UFO/supported_models/overview/)
- [RAG 配置](https://microsoft.github.io/UFO/advanced_usage/reinforce_appagent/overview/)
</td>
</tr>
</table>
---
## 📢 最新更新
### 2025-11 – UFO³ Galaxy 框架发布 🌌
**重大研究突破:** 多设备编排系统
- 🌟 **声明式 DAG 分解**:TaskConstellation 结构实现工作流逻辑和依赖关系
- 🔄 **动态图演化**:通过受控重写适应的活态星座
- 🎯 **异构编排**:基于能力的设备匹配实现安全的异步执行
- 🔌 **统一 AIP 协议**:基于 WebSocket 的安全智能体协调,具有容错能力
- 🛠️ **支持 MCP 的智能体框架**:用于快速设备智能体开发的模板驱动工具包
- 📄 **研究论文**:[UFO³: Weaving the Digital Agent Galaxy](https://arxiv.org/abs/2511.11332)
**核心特性:**
- 首个用于 GUI 智能体的多设备编排框架
- 结果驱动的自适应执行,而非僵化的工作流
- 模型上下文协议(MCP)集成用于工具增强
- 经过形式化验证的正确性和并发安全保证
### 2025-04 – UFO² v2.0.0
- 📅 UFO² 桌面智能体操作系统发布
- 🏗️ 具有 AgentOS 概念的增强架构
- 📄 [技术报告](https://arxiv.org/pdf/2504.14603)发布
- ✅ 进入长期支持(LTS)状态
### 2024-02 – 原始 UFO
- 🎈 第一个 UFO 版本 - Windows 的以 UI 为中心的智能体
- 📄 [原始论文](https://arxiv.org/abs/2402.07939)
- 🌍 广泛的媒体报道和采用
---
## 📚 引用
如果您在研究中使用 UFO³ Galaxy 或 UFO²,请引用相关论文:
### UFO³ Galaxy 框架(2025)
```bibtex
@article{zhang2025ufo3,
title={UFO$^3$: Weaving the Digital Agent Galaxy},
author = {Zhang, Chaoyun and Li, Liqun and Huang, He and Ni, Chiming and Qiao, Bo and Qin, Si and Kang, Yu and Ma, Minghua and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei},
journal = {arXiv preprint arXiv:2511.11332},
year = {2025},
}
```
### UFO² 桌面智能体操作系统(2025)
```bibtex
@article{zhang2025ufo2,
title = {{UFO2: The Desktop AgentOS}},
author = {Zhang, Chaoyun and Huang, He and Ni, Chiming and Mu, Jian and Qin, Si and He, Shilin and Wang, Lu and Yang, Fangkai and Zhao, Pu and Du, Chao and Li, Liqun and Kang, Yu and Jiang, Zhao and Zheng, Suzhen and Wang, Rujia and Qian, Jiaxu and Ma, Minghua and Lou, Jian-Guang and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei},
journal = {arXiv preprint arXiv:2504.14603},
year = {2025}
}
```
### 原始 UFO(2024)
```bibtex
@article{zhang2024ufo,
title = {{UFO: A UI-Focused Agent for Windows OS Interaction}},
author = {Zhang, Chaoyun and Li, Liqun and He, Shilin and Zhang, Xu and Qiao, Bo and Qin, Si and Ma, Minghua and Kang, Yu and Lin, Qingwei and Rajmohan, Saravan and Zhang, Dongmei and Zhang, Qi},
journal = {arXiv preprint arXiv:2402.07939},
year = {2024}
}
```
---
## 🌐 媒体和社区
**媒体报道:**
- [微软正式开源UFO²,Windows桌面迈入「AgentOS 时代」](https://www.jiqizhixin.com/articles/2025-05-06-13)
- [Microsoft's UFO: Smarter Windows Experience](https://the-decoder.com/microsofts-ufo-abducts-traditional-user-interfaces-for-a-smarter-windows-experience/)
- [下一代Windows系统曝光](https://baijiahao.baidu.com/s?id=1790938358152188625)
- **[更多报道 →](./ufo/README_ZH.md#-媒体报道)**
**社区:**
- 💬 [GitHub 讨论](https://github.com/microsoft/UFO/discussions)
- 🐛 [问题跟踪器](https://github.com/microsoft/UFO/issues)
- 📧 电子邮件:[ufo-agent@microsoft.com](mailto:ufo-agent@microsoft.com)
- 📺 [YouTube 频道](https://www.youtube.com/watch?v=QT_OhygMVXU)
---
## 🎨 相关项目和研究
**Microsoft Research:**
- **[TaskWeaver](https://github.com/microsoft/TaskWeaver)** – 用于数据分析和任务自动化的代码优先 LLM 智能体框架
**GUI 智能体研究:**
- **[基于 LLM 的 GUI 智能体综述](https://github.com/vyokky/LLM-Brained-GUI-Agents-Survey)** – GUI 自动化智能体的全面综述
- **[交互式综述网站](https://vyokky.github.io/LLM-Brained-GUI-Agents-Survey/)** – 探索最新的 GUI 智能体研究和发展
**多智能体系统:**
- **UFO³ Galaxy** 代表了多设备编排的新方法,引入了星座框架,用于跨平台协调异构智能体
- 基于多智能体协调研究,同时解决跨设备 GUI 自动化的独特挑战
**基准测试:**
- **[Windows Agent Arena (WAA)](https://github.com/nice-mee/WindowsAgentArena)** – Windows 自动化智能体的评估基准
- **[OSWorld](https://github.com/nice-mee/WindowsAgentArena/tree/2020-qqtcg/osworld)** – 跨应用程序任务评估套件
---
## 💡 常见问题
<details>
<summary><strong>🤔 我应该使用 Galaxy 还是 UFO²?</strong></summary>
**从 UFO² 开始**,如果:
- 您只需要 Windows 自动化
- 您想要快速设置和学习
- 任务相对简单
**选择 Galaxy**,如果:
- 您需要跨设备协调
- 任务复杂且多步骤
- 您想要高级编排
- 您对积极开发感到满意
**混合方法**,如果:
- 您想要两全其美
- 一些任务简单(UFO²),一些复杂(Galaxy)
- 您正在逐步迁移
</details>
<details>
<summary><strong>⚠️ UFO² 会被弃用吗?</strong></summary>
**不会!** UFO² 已进入**长期支持(LTS)**状态:
- ✅ 积极维护
- ✅ 错误修复和安全更新
- ✅ 性能改进
- ✅ 完整的社区支持
- ✅ 没有弃用计划
UFO² 是 Windows 自动化的稳定、经过验证的解决方案。
</details>
<details>
<summary><strong>🔄 如何从 UFO² 迁移到 Galaxy?</strong></summary>
迁移是**渐进的和可选的**:
1. **阶段 1:学习** – 了解 Galaxy 概念
2. **阶段 2:实验** – 尝试使用 Galaxy 进行非关键任务
3. **阶段 3:混合** – 同时使用两个框架
4. **阶段 4:迁移** – 逐步将复杂任务移至 Galaxy
**无强制迁移!** 只要满足您的需求,就继续使用 UFO²。
有关详细信息,请参阅[迁移指南](./documents/docs/getting_started/migration_ufo2_to_galaxy.md)。
</details>
<details>
<summary><strong>🎯 Galaxy 能做 UFO² 做的所有事情吗?</strong></summary>
**功能上:是的。** Galaxy 可以使用 UFO² 作为 Windows 设备智能体。
**实际上:这取决于。**
- 对于**简单的 Windows 任务**:UFO² 独立更简单、更精简
- 对于**复杂工作流**:Galaxy 编排 UFO² 与其他设备智能体
**建议:** 使用正确的工具来完成工作。UFO² 可以独立工作或作为 Galaxy 的 Windows 设备智能体。
</details>
<details>
<summary><strong>📊 Galaxy 有多成熟?</strong></summary>
**状态:积极开发** 🚧
**稳定:**
- ✅ 核心架构
- ✅ DAG 编排
- ✅ 基本多设备支持
- ✅ 事件系统
**开发中:**
- 🔨 高级设备类型
- 🔨 增强监控
- 🔨 性能优化
- 🔨 扩展文档
**建议:** 非常适合实验和非关键工作流。
</details>
<details>
<summary><strong>🔧 我可以扩展或自定义吗?</strong></summary>
**两个框架都是高度可扩展的:**
**UFO²:**
- 自定义操作和自动化器
- 自定义知识源(RAG)
- 自定义控件检测器
- 自定义评估指标
**Galaxy:**
- 自定义智能体
- 自定义设备类型
- 自定义编排策略
- 自定义可视化组件
有关扩展指南,请参阅各自的文档。
</details>
<details>
<summary><strong>🤝 我如何贡献?</strong></summary>
我们欢迎对 UFO² 和 Galaxy 的贡献!
**贡献方式:**
- 🐛 报告错误和问题
- 💡 建议功能和改进
- 📝 改进文档
- 🧪 添加测试和示例
- 🔧 提交拉取请求
有关指南,请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md)。
</details>
---
## ⚠️ 免责声明和许可证
**免责声明:** 使用本软件即表示您承认并同意 [DISCLAIMER.md](./DISCLAIMER.md) 中的条款。
**许可证:** 本项目根据 [MIT 许可证](LICENSE) 授权。
**商标:** Microsoft 商标的使用遵循 [Microsoft 商标指南](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general)。
---
<div align="center">
## 🚀 准备开始了吗?
<table>
<tr>
<td align="center" width="50%">
### 🌌 探索 Galaxy
**多设备编排**
[](./galaxy/README_ZH.md)
</td>
<td align="center" width="50%">
### 🪟 试试 UFO²
**Windows 桌面智能体**
[](./ufo/README_ZH.md)
</td>
</tr>
</table>
---
<sub>© Microsoft 2025 | UFO³ 是一个开源研究项目</sub>
<sub>⭐ 在 GitHub 上给我们加星 | 🤝 贡献 | 📖 阅读文档 | 💬 加入讨论</sub>
</div>
---
<p align="center">
<img src="assets/logo3.png" alt="UFO logo" width="60">
<br>
<em>从单智能体到数字星系</em>
<br>
<strong>UFO³ - 编织智能自动化的未来</strong>
</p>
================================================
FILE: SECURITY.md
================================================
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
================================================
FILE: aip/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Agent Interaction Protocol (AIP)
A lightweight, persistent, and extensible messaging layer for multi-agent orchestration.
AIP provides:
- Long-lived agent sessions spanning multiple task executions
- Low-latency event propagation for dynamic scheduling
- Standardized communication for registration, task dispatch, and result reporting
- Resilient connection handling with automatic reconnection
- Extensible protocol with middleware support
Architecture:
Messages (aip.messages) - Strongly-typed message definitions
↓
Protocol (aip.protocol) - Protocol logic (registration, task execution, heartbeat)
↓
Transport (aip.transport) - Transport abstraction (WebSocket, future: HTTP/3, gRPC)
↓
Endpoints (aip.endpoints) - Endpoint implementations (Device Server, Device Client, Constellation)
↓
Resilience (aip.resilience) - Reconnection, heartbeat, timeout management
Usage:
# Device Server
from aip.endpoints import DeviceServerEndpoint
endpoint = DeviceServerEndpoint(ws_manager, session_manager)
await endpoint.handle_websocket(websocket)
# Device Client
from aip.endpoints import DeviceClientEndpoint
endpoint = DeviceClientEndpoint(ws_url, ufo_client)
await endpoint.start()
# Constellation Client
from aip.endpoints import ConstellationEndpoint
endpoint = ConstellationEndpoint(task_name, message_processor)
await endpoint.connect_to_device(device_info, message_processor)
"""
from . import endpoints, extensions, messages, protocol, resilience, transport
__version__ = "1.0.0"
__all__ = [
"messages",
"transport",
"protocol",
"endpoints",
"resilience",
"extensions",
]
# Convenience exports
from .endpoints import (
ConstellationEndpoint,
DeviceClientEndpoint,
DeviceServerEndpoint,
)
from .messages import (
ClientMessage,
ClientMessageType,
ClientType,
Command,
Result,
ResultStatus,
ServerMessage,
ServerMessageType,
TaskStatus,
)
from .protocol import (
AIPProtocol,
CommandProtocol,
DeviceInfoProtocol,
HeartbeatProtocol,
RegistrationProtocol,
TaskExecutionProtocol,
)
from .resilience import HeartbeatManager, ReconnectionStrategy, TimeoutManager
from .transport import Transport, WebSocketTransport
__all__.extend(
[
# Messages
"ClientMessage",
"ServerMessage",
"ClientMessageType",
"ServerMessageType",
"ClientType",
"TaskStatus",
"Command",
"Result",
"ResultStatus",
# Transport
"Transport",
"WebSocketTransport",
# Protocol
"AIPProtocol",
"RegistrationProtocol",
"TaskExecutionProtocol",
"HeartbeatProtocol",
"DeviceInfoProtocol",
"CommandProtocol",
# Endpoints
"DeviceServerEndpoint",
"DeviceClientEndpoint",
"ConstellationEndpoint",
# Resilience
"ReconnectionStrategy",
"HeartbeatManager",
"TimeoutManager",
]
)
================================================
FILE: aip/endpoints/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Endpoints
Provides endpoint implementations for Device Server, Device Client, and Constellation Client.
"""
from .base import AIPEndpoint
from .client_endpoint import DeviceClientEndpoint
from .constellation_endpoint import ConstellationEndpoint
from .server_endpoint import DeviceServerEndpoint
__all__ = [
"AIPEndpoint",
"DeviceServerEndpoint",
"DeviceClientEndpoint",
"ConstellationEndpoint",
]
================================================
FILE: aip/endpoints/base.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Base AIP Endpoint
Provides the foundation for all AIP endpoint implementations.
"""
import logging
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional
from aip.protocol import AIPProtocol
from aip.resilience import ReconnectionStrategy, TimeoutManager
class AIPEndpoint(ABC):
"""
Abstract base class for AIP endpoints.
An endpoint combines:
- Protocol (message handling)
- Session management (state tracking)
- Resilience (reconnection, heartbeat, timeout)
Subclasses implement specific endpoint types:
- DeviceServerEndpoint: Server-side device connection management
- DeviceClientEndpoint: Client-side device operations
- ConstellationEndpoint: Constellation client operations
"""
def __init__(
self,
protocol: AIPProtocol,
reconnection_strategy: Optional[ReconnectionStrategy] = None,
heartbeat_interval: float = 30.0,
default_timeout: float = 120.0,
):
"""
Initialize AIP endpoint.
:param protocol: AIP protocol instance
:param reconnection_strategy: Optional reconnection strategy
:param heartbeat_interval: Heartbeat interval (seconds)
:param default_timeout: Default timeout for operations (seconds)
"""
self.protocol = protocol
self.logger = logging.getLogger(self.__class__.__name__)
# Resilience components
self.reconnection_strategy = reconnection_strategy or ReconnectionStrategy()
self.timeout_manager = TimeoutManager(default_timeout=default_timeout)
# Session tracking
self.session_handlers: Dict[str, Any] = {}
@abstractmethod
async def start(self) -> None:
"""
Start the endpoint.
Should establish connections, register handlers, and begin listening for messages.
"""
pass
@abstractmethod
async def stop(self) -> None:
"""
Stop the endpoint.
Should gracefully close connections and cleanup resources.
"""
pass
@abstractmethod
async def handle_message(self, msg: Any) -> None:
"""
Handle an incoming message.
:param msg: Message to handle
"""
pass
def is_connected(self) -> bool:
"""
Check if endpoint is connected.
:return: True if connected, False otherwise
"""
return self.protocol.is_connected()
async def send_with_timeout(
self, msg: Any, timeout: Optional[float] = None
) -> None:
"""
Send a message with timeout.
:param msg: Message to send
:param timeout: Optional timeout override
"""
await self.timeout_manager.with_timeout(
self.protocol.send_message(msg), timeout, "send_message"
)
async def receive_with_timeout(
self, message_type: type, timeout: Optional[float] = None
) -> Any:
"""
Receive a message with timeout.
:param message_type: Expected message type
:param timeout: Optional timeout override
:return: Received message
"""
return await self.timeout_manager.with_timeout(
self.protocol.receive_message(message_type), timeout, "receive_message"
)
@abstractmethod
async def reconnect_device(self, device_id: str) -> bool:
"""
Attempt to reconnect to a device.
:param device_id: Device to reconnect to
:return: True if successful, False otherwise
"""
pass
@abstractmethod
async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
"""
Cancel all tasks for a device.
:param device_id: Device ID
:param reason: Cancellation reason
"""
pass
@abstractmethod
async def on_device_disconnected(self, device_id: str) -> None:
"""
Handle device disconnection notification.
:param device_id: Disconnected device ID
"""
pass
================================================
FILE: aip/endpoints/client_endpoint.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Device Client Endpoint
Wraps the existing UFO WebSocket client with AIP protocol abstractions.
"""
import logging
from typing import Any
from aip.endpoints.base import AIPEndpoint
from aip.protocol import AIPProtocol, HeartbeatProtocol, RegistrationProtocol
from aip.resilience import HeartbeatManager, ReconnectionStrategy
from aip.transport.websocket import WebSocketTransport
class DeviceClientEndpoint(AIPEndpoint):
"""
Device Client endpoint for AIP.
Wraps the existing UFOWebSocketClient to provide AIP protocol support
while maintaining full backward compatibility.
"""
def __init__(
self,
ws_url: str,
ufo_client: Any, # UFOClient
max_retries: int = 3,
timeout: float = 120.0,
):
"""
Initialize device client endpoint.
:param ws_url: WebSocket server URL
:param ufo_client: UFOClient instance
:param max_retries: Maximum reconnection retries
:param timeout: Connection timeout
"""
# Import here to avoid circular dependency
from ufo.client.websocket import UFOWebSocketClient
# Create transport and protocol
transport = WebSocketTransport(
ping_interval=20, ping_timeout=180, max_size=100 * 1024 * 1024
)
protocol = AIPProtocol(transport)
# Create specialized protocols
registration_protocol = RegistrationProtocol(transport)
heartbeat_protocol = HeartbeatProtocol(transport)
# Create reconnection strategy
reconnection_strategy = ReconnectionStrategy(
max_retries=max_retries,
initial_backoff=2.0,
max_backoff=60.0,
)
super().__init__(protocol=protocol, reconnection_strategy=reconnection_strategy)
self.ws_url = ws_url
self.ufo_client = ufo_client
self.timeout = timeout
# Use existing client for compatibility
self.client = UFOWebSocketClient(ws_url, ufo_client, max_retries, timeout)
# AIP-specific components
self.registration_protocol = registration_protocol
self.heartbeat_protocol = heartbeat_protocol
self.heartbeat_manager = HeartbeatManager(heartbeat_protocol)
self.logger = logging.getLogger(f"{__name__}.DeviceClientEndpoint")
async def start(self) -> None:
"""
Start the endpoint and connect to server.
"""
self.logger.info(f"Starting device client endpoint: {self.ws_url}")
# Use existing client's connection logic
import asyncio
asyncio.create_task(self.client.connect_and_listen())
# Wait for connection
await self.client.connected_event.wait()
self.logger.info("Device client endpoint connected")
async def stop(self) -> None:
"""Stop the endpoint."""
self.logger.info("Stopping device client endpoint")
# Stop heartbeat
await self.heartbeat_manager.stop_all()
# Close connection
if self.client._ws:
await self.client._ws.close()
await self.protocol.close()
self.logger.info("Device client endpoint stopped")
async def handle_message(self, msg: Any) -> None:
"""
Handle an incoming message.
:param msg: Message to handle
"""
# Messages are handled by the existing client
await self.client.handle_message(msg)
async def reconnect_device(self, device_id: str) -> bool:
"""
Attempt to reconnect.
:param device_id: Device ID (unused for client)
:return: True if successful
"""
try:
await self.start()
return True
except Exception as e:
self.logger.error(f"Reconnection failed: {e}")
return False
async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
"""
Cancel device tasks.
:param device_id: Device ID
:param reason: Cancellation reason
"""
# Client-side task cancellation handled by UFOClient
self.logger.info(f"Cancelling tasks for {device_id}: {reason}")
async def on_device_disconnected(self, device_id: str) -> None:
"""
Handle disconnection.
:param device_id: Device ID
"""
self.logger.warning(f"Device disconnected: {device_id}")
def is_connected(self) -> bool:
"""Check if client is connected."""
return self.client.is_connected()
================================================
FILE: aip/endpoints/constellation_endpoint.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Constellation Client Endpoint
Wraps the existing Galaxy constellation client with AIP protocol abstractions.
"""
import logging
from typing import Any, Dict, Optional
from aip.endpoints.base import AIPEndpoint
from aip.protocol import AIPProtocol, RegistrationProtocol
from aip.resilience import ReconnectionStrategy
from aip.transport.websocket import WebSocketTransport
class ConstellationEndpoint(AIPEndpoint):
"""
Constellation Client endpoint for AIP.
Wraps the existing WebSocketConnectionManager to provide AIP protocol support.
"""
def __init__(
self,
task_name: str,
message_processor: Any = None, # MessageProcessor
):
"""
Initialize constellation endpoint.
:param task_name: Task name for this constellation
:param message_processor: Optional message processor
"""
# Create transport and protocol
transport = WebSocketTransport(
ping_interval=30, ping_timeout=30, max_size=100 * 1024 * 1024
)
protocol = AIPProtocol(transport)
# Create registration protocol
registration_protocol = RegistrationProtocol(transport)
# Create reconnection strategy
reconnection_strategy = ReconnectionStrategy(
max_retries=5, initial_backoff=1.0, max_backoff=60.0
)
super().__init__(protocol=protocol, reconnection_strategy=reconnection_strategy)
self.task_name = task_name
self.message_processor = message_processor
self.registration_protocol = registration_protocol
# Import here to avoid circular dependency
from galaxy.client.components.connection_manager import (
WebSocketConnectionManager,
)
self.connection_manager = WebSocketConnectionManager(task_name)
self.logger = logging.getLogger(f"{__name__}.ConstellationEndpoint")
async def start(self) -> None:
"""Start the endpoint."""
self.logger.info(f"Constellation endpoint started for {self.task_name}")
async def stop(self) -> None:
"""Stop the endpoint and disconnect all devices."""
self.logger.info("Stopping constellation endpoint")
await self.connection_manager.disconnect_all()
await self.protocol.close()
async def connect_to_device(
self, device_info: Any, message_processor: Any = None
) -> Any:
"""
Connect to a device.
:param device_info: AgentProfile with device information
:param message_processor: Optional message processor
:return: WebSocket connection
"""
processor = message_processor or self.message_processor
return await self.connection_manager.connect_to_device(device_info, processor)
async def send_task_to_device(self, device_id: str, task_request: Any) -> Any:
"""
Send task to device.
:param device_id: Target device ID
:param task_request: Task request details
:return: Execution result
"""
return await self.connection_manager.send_task_to_device(
device_id, task_request
)
async def request_device_info(self, device_id: str) -> Optional[Dict[str, Any]]:
"""
Request device information.
:param device_id: Device ID
:return: Device info dictionary or None
"""
return await self.connection_manager.request_device_info(device_id)
async def disconnect_device(self, device_id: str) -> None:
"""
Disconnect from a device.
:param device_id: Device ID
"""
await self.connection_manager.disconnect_device(device_id)
def is_device_connected(self, device_id: str) -> bool:
"""
Check if device is connected.
:param device_id: Device ID
:return: True if connected
"""
return self.connection_manager.is_connected(device_id)
async def handle_message(self, msg: Any) -> None:
"""
Handle incoming message.
:param msg: Message to handle
"""
# Messages handled by message processor
if self.message_processor:
await self.message_processor.process_message(msg)
async def reconnect_device(self, device_id: str) -> bool:
"""
Attempt to reconnect to device.
:param device_id: Device ID
:return: True if successful
"""
try:
# Get device info from somewhere
# This would need to be implemented based on available device registry
self.logger.warning(f"Reconnection for {device_id} not fully implemented")
return False
except Exception as e:
self.logger.error(f"Reconnection failed for {device_id}: {e}")
return False
async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
"""
Cancel tasks for device.
:param device_id: Device ID
:param reason: Cancellation reason
"""
# Cancel pending tasks managed by connection manager
self.connection_manager._cancel_pending_tasks_for_device(device_id)
self.logger.info(f"Cancelled tasks for {device_id}: {reason}")
async def on_device_disconnected(self, device_id: str) -> None:
"""
Handle device disconnection.
:param device_id: Device ID
"""
self.logger.warning(f"Device {device_id} disconnected from constellation")
await self.cancel_device_tasks(device_id, "device_disconnected")
================================================
FILE: aip/endpoints/server_endpoint.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Device Server Endpoint
Wraps the existing UFO server WebSocket handler with AIP protocol abstractions.
This maintains backward compatibility while providing the AIP interface.
"""
import logging
from typing import Any, Optional
from fastapi import WebSocket
from aip.endpoints.base import AIPEndpoint
from aip.protocol import AIPProtocol
from aip.resilience import ReconnectionStrategy
class DeviceServerEndpoint(AIPEndpoint):
"""
Device Server endpoint for AIP.
Wraps the existing UFOWebSocketHandler to provide AIP protocol support
while maintaining full backward compatibility with existing implementations.
"""
def __init__(
self,
ws_manager: Any, # WSManager
session_manager: Any, # SessionManager
local: bool = False,
protocol: Optional[AIPProtocol] = None,
reconnection_strategy: Optional[ReconnectionStrategy] = None,
):
"""
Initialize device server endpoint.
:param ws_manager: WebSocket manager instance
:param session_manager: Session manager instance
:param local: Whether running in local mode
:param protocol: Optional AIP protocol instance
:param reconnection_strategy: Optional reconnection strategy
"""
# Import here to avoid circular dependency
from ufo.server.ws.handler import UFOWebSocketHandler
if protocol is None:
# Create a minimal protocol for compatibility
from aip.transport.websocket import WebSocketTransport
protocol = AIPProtocol(WebSocketTransport())
super().__init__(protocol=protocol, reconnection_strategy=reconnection_strategy)
self.ws_manager = ws_manager
self.session_manager = session_manager
self.local = local
# Use existing handler for actual implementation
self.handler = UFOWebSocketHandler(ws_manager, session_manager, local)
self.logger = logging.getLogger(f"{__name__}.DeviceServerEndpoint")
async def start(self) -> None:
"""
Start the endpoint.
Note: For server endpoints, connections are handled per WebSocket.
"""
self.logger.info("Device server endpoint ready")
async def stop(self) -> None:
"""Stop the endpoint."""
self.logger.info("Device server endpoint stopped")
async def handle_websocket(self, websocket: WebSocket) -> None:
"""
Handle a WebSocket connection.
This delegates to the existing UFOWebSocketHandler for full compatibility.
:param websocket: WebSocket connection
"""
await self.handler.handler(websocket)
async def handle_message(self, msg: Any) -> None:
"""
Handle an incoming message.
:param msg: Message to handle
"""
# Messages are handled within the handler per connection
pass
async def reconnect_device(self, device_id: str) -> bool:
"""
Server-side reconnection is handled by client reconnecting.
:param device_id: Device ID
:return: False (server waits for client)
"""
self.logger.debug(f"Server endpoint does not actively reconnect to {device_id}")
return False
async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
"""
Cancel all tasks for a device.
:param device_id: Device ID
:param reason: Cancellation reason
"""
session_ids = self.ws_manager.get_device_sessions(device_id)
for session_id in session_ids:
try:
await self.session_manager.cancel_task(session_id, reason=reason)
except Exception as e:
self.logger.error(f"Error cancelling session {session_id}: {e}")
async def on_device_disconnected(self, device_id: str) -> None:
"""
Handle device disconnection notification.
:param device_id: Disconnected device ID
"""
self.logger.info(f"Device {device_id} disconnected")
================================================
FILE: aip/extensions/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Extension Support
Provides extension points for customizing AIP behavior.
"""
from .base import AIPExtension
from .middleware import LoggingExtension, MetricsExtension
__all__ = ["AIPExtension", "LoggingExtension", "MetricsExtension"]
================================================
FILE: aip/extensions/base.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Base Extension Interface
Defines the interface for AIP extensions.
"""
from abc import ABC, abstractmethod
from typing import Any
class AIPExtension(ABC):
"""
Abstract base class for AIP extensions.
Extensions can customize protocol behavior, add logging,
collect metrics, or implement custom business logic.
"""
@abstractmethod
async def on_message_sent(self, msg: Any) -> None:
"""
Called when a message is sent.
:param msg: Message that was sent
"""
pass
@abstractmethod
async def on_message_received(self, msg: Any) -> None:
"""
Called when a message is received.
:param msg: Message that was received
"""
pass
@abstractmethod
async def on_connection_established(self, endpoint_id: str) -> None:
"""
Called when a connection is established.
:param endpoint_id: Endpoint identifier
"""
pass
@abstractmethod
async def on_connection_closed(self, endpoint_id: str) -> None:
"""
Called when a connection is closed.
:param endpoint_id: Endpoint identifier
"""
pass
@abstractmethod
async def on_error(self, error: Exception, context: str) -> None:
"""
Called when an error occurs.
:param error: Exception that occurred
:param context: Context where error occurred
"""
pass
================================================
FILE: aip/extensions/middleware.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Extension Middleware
Provides ready-to-use extensions for common use cases.
"""
import logging
import time
from typing import Any, Dict
from aip.extensions.base import AIPExtension
class LoggingExtension(AIPExtension):
"""
Extension that logs all protocol events.
"""
def __init__(self, log_level: int = logging.INFO):
"""
Initialize logging extension.
:param log_level: Log level for events
"""
self.logger = logging.getLogger(f"{__name__}.LoggingExtension")
self.log_level = log_level
async def on_message_sent(self, msg: Any) -> None:
"""Log sent message."""
msg_type = getattr(msg, "type", "unknown")
self.logger.log(self.log_level, f"[SENT] {msg_type}")
async def on_message_received(self, msg: Any) -> None:
"""Log received message."""
msg_type = getattr(msg, "type", "unknown")
self.logger.log(self.log_level, f"[RECV] {msg_type}")
async def on_connection_established(self, endpoint_id: str) -> None:
"""Log connection establishment."""
self.logger.log(self.log_level, f"[CONN] Connection established: {endpoint_id}")
async def on_connection_closed(self, endpoint_id: str) -> None:
"""Log connection closure."""
self.logger.log(self.log_level, f"[DISC] Connection closed: {endpoint_id}")
async def on_error(self, error: Exception, context: str) -> None:
"""Log error."""
self.logger.error(f"[ERROR] {context}: {error}", exc_info=True)
class MetricsExtension(AIPExtension):
"""
Extension that collects protocol metrics.
"""
def __init__(self):
"""Initialize metrics extension."""
self.logger = logging.getLogger(f"{__name__}.MetricsExtension")
self.metrics: Dict[str, Any] = {
"messages_sent": 0,
"messages_received": 0,
"connections_established": 0,
"connections_closed": 0,
"errors": 0,
"message_types": {},
"latencies": [],
}
self._message_timestamps: Dict[str, float] = {}
async def on_message_sent(self, msg: Any) -> None:
"""Track sent message."""
self.metrics["messages_sent"] += 1
msg_type = str(getattr(msg, "type", "unknown"))
self.metrics["message_types"][msg_type] = (
self.metrics["message_types"].get(msg_type, 0) + 1
)
# Track timestamp for latency calculation
msg_id = getattr(msg, "request_id", None) or getattr(msg, "response_id", None)
if msg_id:
self._message_timestamps[msg_id] = time.time()
async def on_message_received(self, msg: Any) -> None:
"""Track received message."""
self.metrics["messages_received"] += 1
# Calculate latency if we have a matching sent message
msg_id = getattr(msg, "request_id", None) or getattr(msg, "response_id", None)
if msg_id and msg_id in self._message_timestamps:
latency = time.time() - self._message_timestamps[msg_id]
self.metrics["latencies"].append(latency)
del self._message_timestamps[msg_id]
async def on_connection_established(self, endpoint_id: str) -> None:
"""Track connection establishment."""
self.metrics["connections_established"] += 1
async def on_connection_closed(self, endpoint_id: str) -> None:
"""Track connection closure."""
self.metrics["connections_closed"] += 1
async def on_error(self, error: Exception, context: str) -> None:
"""Track error."""
self.metrics["errors"] += 1
def get_metrics(self) -> Dict[str, Any]:
"""
Get collected metrics.
:return: Metrics dictionary
"""
metrics = self.metrics.copy()
if metrics["latencies"]:
metrics["avg_latency"] = sum(metrics["latencies"]) / len(
metrics["latencies"]
)
metrics["max_latency"] = max(metrics["latencies"])
metrics["min_latency"] = min(metrics["latencies"])
return metrics
def reset_metrics(self) -> None:
"""Reset all metrics."""
self.metrics = {
"messages_sent": 0,
"messages_received": 0,
"connections_established": 0,
"connections_closed": 0,
"errors": 0,
"message_types": {},
"latencies": [],
}
self._message_timestamps.clear()
================================================
FILE: aip/messages.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Agent Interaction Protocol (AIP) - Message Definitions
This module defines the core message types and structures used in the Agent Interaction Protocol.
Messages are strongly typed using Pydantic for validation and serialization.
Message Flow:
Client → Server: ClientMessage (REGISTER, TASK, HEARTBEAT, COMMAND_RESULTS, etc.)
Server → Client: ServerMessage (TASK, COMMAND, TASK_END, HEARTBEAT, etc.)
Key Concepts:
- ClientType: Distinguishes between device agents and constellation clients
- MessageType: Defines the purpose of each message
- TaskStatus: Tracks the state of task execution
- Result: Encapsulates command execution outcomes
"""
from enum import Enum
from typing import Any, Dict, List, Literal, Optional
from pydantic import BaseModel, ConfigDict, Field
from ufo.client.mcp.mcp_server_manager import BaseMCPServer
# ============================================================================
# Core Data Structures
# ============================================================================
class Rect(BaseModel):
"""
Rectangle coordinates for UI elements.
Represents a rectangle with x, y coordinates and width and height.
"""
x: int
y: int
width: int
height: int
class ControlInfo(BaseModel):
"""
Information about a UI control.
"""
annotation_id: Optional[str] = None
name: Optional[str] = None
title: Optional[str] = None
handle: Optional[int] = None
class_name: Optional[str] = None
rectangle: Optional[Rect] = None
control_type: Optional[str] = None
automation_id: Optional[str] = None
is_enabled: Optional[bool] = None
is_visible: Optional[bool] = None
source: Optional[str] = None
text_content: Optional[str] = None
class WindowInfo(ControlInfo):
"""
Information about a window in the UI.
"""
process_id: Optional[int] = None
process_name: Optional[str] = None
is_visible: Optional[bool] = None
is_minimized: Optional[bool] = None
is_maximized: Optional[bool] = None
is_active: Optional[bool] = None
class AppWindowControlInfo(BaseModel):
"""
Information about a window and its controls.
"""
window_info: WindowInfo
controls: Optional[List[ControlInfo]] = None
# ============================================================================
# Tool and Command Structures
# ============================================================================
class MCPToolInfo(BaseModel):
"""
Information about a tool registered with the computer.
"""
tool_key: str
tool_name: str
title: Optional[str] = None
namespace: str
tool_type: str
description: Optional[str] = None
input_schema: Optional[Dict[str, Any]] = None
output_schema: Optional[Dict[str, Any]] = None
meta: Optional[Dict[str, Any]] = None
annotations: Optional[Dict[str, Any]] = None
class MCPToolCall(BaseModel):
"""
Information about a tool registered with the computer and its associated MCP server.
"""
tool_key: str # Unique key for the tool, e.g., "namespace.tool_name"
tool_name: str # Name of the tool
title: Optional[str] = None # Title of the tool, if any
namespace: str # Namespace of the tool, same as the MCP server namespace
tool_type: str # Type of the tool (e.g., "action", "data_collection")
description: str # Description of the tool
input_schema: Optional[Dict[str, Any]] = None # Input schema for the tool, if any
output_schema: Optional[Dict[str, Any]] = None # Output schema for the tool, if any
parameters: Optional[Dict[str, Any]] = None # Parameters for the tool, if any
mcp_server: BaseMCPServer # The BaseMCPServer instance where the tool is registered
meta: Optional[Dict[str, Any]] = None # Metadata about the tool, if any
annotations: Optional[Dict[str, Any]] = None # Annotations for the tool, if any
model_config = ConfigDict(arbitrary_types_allowed=True)
@property
def tool_info(self) -> MCPToolInfo:
"""
Get a dictionary representation of the tool call.
:return: Dictionary with tool information.
"""
return MCPToolInfo(
tool_key=self.tool_key,
tool_name=self.tool_name,
title=self.title,
namespace=self.namespace,
tool_type=self.tool_type,
description=self.description,
input_schema=self.input_schema,
output_schema=self.output_schema,
meta=self.meta,
annotations=self.annotations,
)
class Command(BaseModel):
"""
Represents a command to be executed by an agent.
Commands are atomic units of work dispatched by the orchestrator.
"""
tool_name: str = Field(..., description="Name of the tool to execute")
parameters: Optional[Dict[str, Any]] = Field(
default=None, description="Parameters for the tool"
)
tool_type: Literal["data_collection", "action"] = Field(
..., description="Type of tool: data_collection or action"
)
call_id: Optional[str] = Field(
default=None, description="Unique identifier for this command call"
)
# ============================================================================
# Result and Status Enums
# ============================================================================
class ResultStatus(str, Enum):
"""
Represents the status of a command execution result.
"""
SUCCESS = "success"
FAILURE = "failure"
SKIPPED = "skipped"
NONE = "none"
class Result(BaseModel):
"""
Represents the result of a command execution.
Contains status, error information, and the actual result payload.
"""
status: ResultStatus = Field(..., description="Execution status")
error: Optional[str] = Field(default=None, description="Error message if failed")
result: Any = Field(default=None, description="Result payload")
namespace: Optional[str] = Field(
default=None, description="Namespace of the executed tool"
)
call_id: Optional[str] = Field(
default=None, description="ID matching the Command.call_id"
)
class TaskStatus(str, Enum):
"""
Represents the status of a task in the AIP protocol.
States:
CONTINUE: Task is ongoing, more steps needed
COMPLETED: Task finished successfully
FAILED: Task encountered an error
OK: Acknowledgment or health check passed
ERROR: Protocol-level error occurred
"""
CONTINUE = "continue"
COMPLETED = "completed"
FAILED = "failed"
OK = "ok"
ERROR = "error"
# ============================================================================
# Message Type Enums
# ============================================================================
class ClientMessageType(str, Enum):
"""
Message types sent from client to server.
Registration & Health:
REGISTER: Initial registration with server
HEARTBEAT: Periodic keepalive signal
Task Execution:
TASK: Request to execute a task
TASK_END: Notify task completion
COMMAND_RESULTS: Return results of executed commands
Device Info:
DEVICE_INFO_REQUEST: Request device information
DEVICE_INFO_RESPONSE: Response with device information
Error Handling:
ERROR: Report an error condition
"""
TASK = "task"
HEARTBEAT = "heartbeat"
COMMAND_RESULTS = "command_results"
ERROR = "error"
REGISTER = "register"
TASK_END = "task_end"
DEVICE_INFO_REQUEST = "device_info_request"
DEVICE_INFO_RESPONSE = "device_info_response"
class ServerMessageType(str, Enum):
"""
Message types sent from server to client.
Task Execution:
TASK: Task assignment to device
COMMAND: Command(s) to execute
TASK_END: Task completion notification
Health & Info:
HEARTBEAT: Keepalive acknowledgment
DEVICE_INFO_REQUEST: Request for device information
DEVICE_INFO_RESPONSE: Device information response
Error Handling:
ERROR: Error notification
"""
TASK = "task"
HEARTBEAT = "heartbeat"
TASK_END = "task_end"
COMMAND = "command"
ERROR = "error"
DEVICE_INFO_REQUEST = "device_info_request"
DEVICE_INFO_RESPONSE = "device_info_response"
class ClientType(str, Enum):
"""
Type of client in the AIP system.
DEVICE: A device agent that executes tasks
CONSTELLATION: An orchestrator that manages multiple devices
"""
DEVICE = "device"
CONSTELLATION = "constellation"
# ============================================================================
# Core Message Classes
# ============================================================================
class ServerMessage(BaseModel):
"""
Message sent from server to client.
Represents all server-to-client communications including task assignments,
command dispatches, heartbeats, and error notifications.
Fields:
type: Message type (TASK, COMMAND, HEARTBEAT, etc.)
status: Task status (CONTINUE, COMPLETED, FAILED, OK, ERROR)
user_request: Original user request text
agent_name: Name of the agent handling the task
process_name: Process name for execution context
root_name: Root application name
actions: List of commands to execute
messages: List of message strings (e.g., logs)
error: Error description if status is ERROR
session_id: Unique session identifier
task_name: Human-readable task name
timestamp: ISO 8601 timestamp
response_id: Unique response identifier for correlation
result: Result payload for TASK_END or DEVICE_INFO_RESPONSE
"""
type: ServerMessageType = Field(..., description="Type of server message")
status: TaskStatus = Field(..., description="Current task status")
user_request: Optional[str] = Field(
default=None, description="Original user request"
)
agent_name: Optional[str] = Field(default=None, description="Agent name")
process_name: Optional[str] = Field(default=None, description="Process name")
root_name: Optional[str] = Field(default=None, description="Root application name")
actions: Optional[List[Command]] = Field(
default=None, description="Commands to execute"
)
messages: Optional[List[str]] = Field(default=None, description="Log messages")
error: Optional[str] = Field(default=None, description="Error message")
session_id: Optional[str] = Field(default=None, description="Session ID")
task_name: Optional[str] = Field(default=None, description="Task name")
timestamp: Optional[str] = Field(default=None, description="ISO 8601 timestamp")
response_id: Optional[str] = Field(default=None, description="Unique response ID")
result: Optional[Any] = Field(default=None, description="Result payload")
class ClientMessage(BaseModel):
"""
Message sent from client to server.
Represents all client-to-server communications including registration,
task requests, command results, heartbeats, and error reports.
Fields:
type: Message type (REGISTER, TASK, HEARTBEAT, etc.)
status: Task status
client_type: Type of client (DEVICE or CONSTELLATION)
session_id: Unique session identifier
task_name: Human-readable task name
client_id: Unique client identifier
target_id: Target device ID (for constellation clients)
request: Request text (for TASK messages)
action_results: Results of executed commands
timestamp: ISO 8601 timestamp
request_id: Unique request identifier
prev_response_id: Previous response ID for correlation
error: Error message
metadata: Additional metadata (e.g., system info, capabilities)
"""
type: ClientMessageType = Field(..., description="Type of client message")
status: TaskStatus = Field(..., description="Current task status")
client_type: ClientType = Field(
default=ClientType.DEVICE, description="Type of client"
)
session_id: Optional[str] = Field(default=None, description="Session ID")
task_name: Optional[str] = Field(default=None, description="Task name")
client_id: Optional[str] = Field(default=None, description="Client ID")
target_id: Optional[str] = Field(
default=None, description="Target device ID (for constellation)"
)
request: Optional[str] = Field(default=None, description="Request text")
action_results: Optional[List[Result]] = Field(
default=None, description="Command execution results"
)
timestamp: Optional[str] = Field(default=None, description="ISO 8601 timestamp")
request_id: Optional[str] = Field(default=None, description="Unique request ID")
prev_response_id: Optional[str] = Field(
default=None, description="Previous response ID"
)
error: Optional[str] = Field(default=None, description="Error message")
metadata: Optional[Dict[str, Any]] = Field(
default=None, description="Additional metadata"
)
# ============================================================================
# Message Validation and Utilities
# ============================================================================
class MessageValidator:
"""
Validates AIP messages for protocol compliance.
Provides static methods to validate message structures, required fields,
and protocol-level constraints.
"""
@staticmethod
def validate_registration(msg: ClientMessage) -> bool:
"""
Validate a registration message.
:param msg: Client message to validate
:return: True if valid, False otherwise
"""
if msg.type != ClientMessageType.REGISTER:
return False
if not msg.client_id:
return False
if msg.client_type == ClientType.CONSTELLATION and not msg.target_id:
# Constellation clients should specify target device
pass # Optional, can be set later
return True
@staticmethod
def validate_task_request(msg: ClientMessage) -> bool:
"""
Validate a task request message.
:param msg: Client message to validate
:return: True if valid, False otherwise
"""
if msg.type != ClientMessageType.TASK:
return False
if not msg.request:
return False
if not msg.client_id:
return False
return True
@staticmethod
def validate_command_results(msg: ClientMessage) -> bool:
"""
Validate a command results message.
:param msg: Client message to validate
:return: True if valid, False otherwise
"""
if msg.type != ClientMessageType.COMMAND_RESULTS:
return False
if not msg.prev_response_id:
return False
if msg.action_results is None:
return False
return True
@staticmethod
def validate_server_message(msg: ServerMessage) -> bool:
"""
Validate a server message.
:param msg: Server message to validate
:return: True if valid, False otherwise
"""
# Basic validation
if not msg.type:
return False
if not msg.status:
return False
# Type-specific validation
if msg.type == ServerMessageType.COMMAND:
if not msg.actions:
return False
if not msg.response_id:
return False
return True
# ============================================================================
# Binary Transfer Message Types (New Feature)
# ============================================================================
class BinaryMetadata(BaseModel):
"""
Metadata for binary data transfer.
This metadata is sent as a text frame before the actual binary data,
allowing receivers to prepare for and validate incoming binary transfers.
"""
type: Literal["binary_data"] = "binary_data"
filename: Optional[str] = None
mime_type: Optional[str] = None
size: int = Field(..., description="Size of binary data in bytes")
checksum: Optional[str] = Field(
None, description="MD5 or SHA256 checksum for validation"
)
session_id: Optional[str] = None
description: Optional[str] = None
timestamp: Optional[str] = None
# Allow additional custom fields
model_config = ConfigDict(extra="allow")
class FileTransferStart(BaseModel):
"""
Message to initiate a chunked file transfer.
Sent before sending file chunks to inform the receiver about
the file details and transfer parameters.
"""
type: Literal["file_transfer_start"] = "file_transfer_start"
filename: str = Field(..., description="Name of file being transferred")
size: int = Field(..., description="Total file size in bytes")
chunk_size: int = Field(..., description="Size of each chunk in bytes")
total_chunks: int = Field(..., description="Total number of chunks")
mime_type: Optional[str] = Field(None, description="MIME type of file")
session_id: Optional[str] = None
description: Optional[str] = None
# Allow additional custom fields
model_config = ConfigDict(extra="allow")
class FileTransferComplete(BaseModel):
"""
Message to signal completion of a chunked file transfer.
Sent after all file chunks have been transmitted, includes
checksum for validation.
"""
type: Literal["file_transfer_complete"] = "file_transfer_complete"
filename: str = Field(..., description="Name of transferred file")
total_chunks: int = Field(..., description="Total chunks sent")
checksum: Optional[str] = Field(None, description="MD5 checksum of complete file")
session_id: Optional[str] = None
# Allow additional custom fields
model_config = ConfigDict(extra="allow")
class ChunkMetadata(BaseModel):
"""
Metadata for a single file chunk.
Sent with each chunk during chunked file transfer to track
chunk sequence and validate chunk integrity.
"""
chunk_num: int = Field(..., description="Chunk sequence number (0-indexed)")
chunk_size: int = Field(..., description="Size of this chunk in bytes")
checksum: Optional[str] = Field(None, description="Checksum of this chunk")
# Allow additional custom fields
model_config = ConfigDict(extra="allow")
================================================
FILE: aip/protocol/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Protocol Layer
Implements the core protocol logic for the Agent Interaction Protocol.
"""
from .base import AIPProtocol, MessageHandler, ProtocolHandler
from .command import CommandProtocol
from .device_info import DeviceInfoProtocol
from .heartbeat import HeartbeatProtocol
from .registration import RegistrationProtocol
from .task_execution import TaskExecutionProtocol
__all__ = [
"AIPProtocol",
"MessageHandler",
"ProtocolHandler",
"RegistrationProtocol",
"TaskExecutionProtocol",
"HeartbeatProtocol",
"DeviceInfoProtocol",
"CommandProtocol",
]
================================================
FILE: aip/protocol/base.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Base Protocol Implementation
Provides the core AIP protocol abstractions and message handling infrastructure.
"""
import logging
from abc import ABC, abstractmethod
from typing import Any, Awaitable, Callable, Dict, List, Optional
from aip.messages import ServerMessage
from aip.transport import Transport
# Type aliases for clarity
MessageHandler = Callable[[Any], Awaitable[None]]
ProtocolHandler = Callable[[Any], Awaitable[Optional[Any]]]
class AIPProtocol:
"""
Core AIP protocol implementation.
This class provides the foundation for all AIP communication:
- Message serialization and deserialization
- Middleware pipeline for extensibility
- Message routing and handler registration
- Error handling and logging
The protocol is transport-agnostic and works with any Transport implementation.
Usage:
transport = WebSocketTransport()
protocol = AIPProtocol(transport)
await protocol.send_message(ClientMessage(...))
message = await protocol.receive_message()
"""
def __init__(self, transport: Transport):
"""
Initialize AIP protocol.
:param transport: Transport layer for sending/receiving messages
"""
self.transport = transport
self.message_handlers: Dict[str, List[MessageHandler]] = {}
self.middleware_chain: List["ProtocolMiddleware"] = []
self.logger = logging.getLogger(f"{__name__}.AIPProtocol")
async def send_message(self, msg: Any) -> None:
"""
Send a message through the protocol.
Applies outgoing middleware, serializes the message, and sends via transport.
:param msg: Message to send (ClientMessage or ServerMessage)
:raises: ConnectionError if transport not connected
:raises: IOError if send fails
"""
try:
# Apply outgoing middleware
for middleware in self.middleware_chain:
msg = await middleware.process_outgoing(msg)
# Serialize message
if hasattr(msg, "model_dump_json"):
# Pydantic model
serialized = msg.model_dump_json().encode("utf-8")
elif isinstance(msg, str):
serialized = msg.encode("utf-8")
elif isinstance(msg, bytes):
serialized = msg
else:
raise ValueError(f"Unsupported message type: {type(msg)}")
# Send via transport
await self.transport.send(serialized)
self.logger.debug(f"Sent message: {msg.__class__.__name__}")
except (ConnectionError, IOError, OSError) as e:
# Connection closed or I/O error - this is common during disconnection
# Log at DEBUG level to avoid alarming ERROR logs during normal shutdown
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot send message (connection closed): {e}")
else:
self.logger.warning(f"Connection error sending message: {e}")
raise
except Exception as e:
self.logger.error(f"Error sending message: {e}")
raise
async def receive_message(self, message_type: type = ServerMessage) -> Any:
"""
Receive a message through the protocol.
Receives data from transport, deserializes, and applies incoming middleware.
:param message_type: Expected message type (ClientMessage or ServerMessage)
:return: Deserialized message
:raises: ConnectionError if transport not connected
:raises: IOError if receive fails
"""
try:
# Receive via transport
data = await self.transport.receive()
# Deserialize message
if isinstance(data, bytes):
data = data.decode("utf-8")
if hasattr(message_type, "model_validate_json"):
# Pydantic model
msg = message_type.model_validate_json(data)
else:
raise ValueError(f"Unsupported message type: {message_type}")
# Apply incoming middleware
for middleware in reversed(self.middleware_chain):
msg = await middleware.process_incoming(msg)
self.logger.debug(f"Received message: {msg.__class__.__name__}")
return msg
except (ConnectionError, IOError, OSError) as e:
# Connection closed or I/O error - this is common during disconnection
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot receive message (connection closed): {e}")
else:
self.logger.warning(f"Connection error receiving message: {e}")
raise
except Exception as e:
self.logger.error(f"Error receiving message: {e}")
raise
def add_middleware(self, middleware: "ProtocolMiddleware") -> None:
"""
Add middleware to the protocol pipeline.
Middleware is applied in order for outgoing messages,
and in reverse order for incoming messages.
:param middleware: Middleware to add
"""
self.middleware_chain.append(middleware)
self.logger.info(f"Added middleware: {middleware.__class__.__name__}")
def register_handler(self, message_type: str, handler: MessageHandler) -> None:
"""
Register a handler for a specific message type.
:param message_type: Message type string (e.g., "task", "heartbeat")
:param handler: Async function to handle the message
"""
if message_type not in self.message_handlers:
self.message_handlers[message_type] = []
self.message_handlers[message_type].append(handler)
self.logger.debug(f"Registered handler for: {message_type}")
async def dispatch_message(self, msg: Any) -> None:
"""
Dispatch a message to registered handlers.
:param msg: Message to dispatch
"""
msg_type = getattr(msg, "type", None)
if msg_type and msg_type in self.message_handlers:
for handler in self.message_handlers[msg_type]:
try:
await handler(msg)
except Exception as e:
self.logger.error(
f"Error in handler for {msg_type}: {e}", exc_info=True
)
else:
self.logger.warning(f"No handler for message type: {msg_type}")
def is_connected(self) -> bool:
"""Check if protocol transport is connected."""
return self.transport.is_connected
async def send_error(
self, error_msg: str, response_id: Optional[str] = None
) -> None:
"""
Send a generic error message (server-side).
:param error_msg: Error message
:param response_id: Optional response ID for correlation
"""
import datetime
import uuid
from aip.messages import ServerMessage, ServerMessageType, TaskStatus
error_message = ServerMessage(
type=ServerMessageType.ERROR,
status=TaskStatus.ERROR,
error=error_msg,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=response_id or str(uuid.uuid4()),
)
await self.send_message(error_message)
async def send_ack(
self, session_id: Optional[str] = None, response_id: Optional[str] = None
) -> None:
"""
Send a generic acknowledgment message (server-side).
:param session_id: Optional session ID
:param response_id: Optional response ID for correlation
"""
import datetime
import uuid
from aip.messages import ServerMessage, ServerMessageType, TaskStatus
ack_message = ServerMessage(
type=ServerMessageType.HEARTBEAT,
status=TaskStatus.OK,
session_id=session_id,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=response_id or str(uuid.uuid4()),
)
await self.send_message(ack_message)
async def close(self) -> None:
"""Close protocol and transport."""
await self.transport.close()
# ========================================================================
# Binary Message Handling (New Feature)
# ========================================================================
async def send_binary_message(
self, data: bytes, metadata: Optional[Dict[str, Any]] = None
) -> None:
"""
Send a binary message with optional metadata.
Uses a two-frame approach for structured binary transfers:
1. Text frame with JSON metadata (filename, size, mime_type, checksum, etc.)
2. Binary frame with actual file data
This approach allows receivers to prepare for incoming binary data
and validate it after reception.
:param data: Binary data to send (image, file, etc.)
:param metadata: Optional metadata dict with fields like:
- filename: str
- mime_type: str (e.g., "image/png", "application/pdf")
- size: int (will be auto-filled)
- checksum: str (optional, for validation)
- session_id: str (optional)
- custom fields as needed
:raises: ConnectionError if transport not connected
:raises: IOError if send fails
Example:
# Send an image with metadata
with open("screenshot.png", "rb") as f:
image_data = f.read()
await protocol.send_binary_message(
data=image_data,
metadata={
"filename": "screenshot.png",
"mime_type": "image/png",
"description": "Desktop screenshot"
}
)
"""
import datetime
import json
try:
# 1. Prepare and send metadata as text frame
meta = metadata or {}
meta.update(
{
"type": "binary_data",
"size": len(data),
"timestamp": datetime.datetime.now(
datetime.timezone.utc
).isoformat(),
}
)
meta_json = json.dumps(meta)
await self.transport.send(meta_json.encode("utf-8"))
self.logger.debug(f"Sent binary metadata: {meta}")
# 2. Send actual data as binary frame
await self.transport.send_binary(data)
self.logger.debug(f"Sent {len(data)} bytes of binary data")
except Exception as e:
self.logger.error(f"Error sending binary message: {e}")
raise
async def receive_binary_message(
self, validate_size: bool = True
) -> tuple[bytes, Dict[str, Any]]:
"""
Receive a binary message with metadata.
Expects a two-frame sequence:
1. Text frame with JSON metadata
2. Binary frame with actual data
:param validate_size: If True, validates received size matches metadata
:return: Tuple of (binary_data, metadata_dict)
:raises: ConnectionError if connection closed
:raises: IOError if receive fails
:raises: ValueError if size validation fails
Example:
# Receive a binary file
data, metadata = await protocol.receive_binary_message()
filename = metadata.get("filename", "received_file.bin")
with open(filename, "wb") as f:
f.write(data)
print(f"Received: {filename} ({len(data)} bytes)")
"""
import json
try:
# 1. Receive metadata as text frame
meta_bytes = await self.transport.receive()
meta = json.loads(meta_bytes.decode("utf-8"))
self.logger.debug(f"Received binary metadata: {meta}")
# Validate metadata type
if meta.get("type") != "binary_data":
self.logger.warning(
f"Expected binary_data message, got: {meta.get('type')}"
)
# 2. Receive actual binary data
data = await self.transport.receive_binary()
self.logger.debug(f"Received {len(data)} bytes of binary data")
# 3. Validate size if requested
if validate_size and "size" in meta:
expected_size = meta["size"]
actual_size = len(data)
if actual_size != expected_size:
error_msg = (
f"Size mismatch: expected {expected_size} bytes, "
f"got {actual_size} bytes"
)
self.logger.error(error_msg)
raise ValueError(error_msg)
return data, meta
except Exception as e:
self.logger.error(f"Error receiving binary message: {e}")
raise
async def send_file(
self,
file_path: str,
chunk_size: int = 1024 * 1024, # 1MB chunks
compute_checksum: bool = True,
) -> None:
"""
Send a file in chunks (for large files).
Sends large files by splitting them into chunks and sending
a completion message with checksum for validation.
Protocol:
1. Send file_transfer_start message (text frame)
2. Send file chunks as binary messages
3. Send file_transfer_complete message with checksum (text frame)
:param file_path: Path to file to send
:param chunk_size: Size of each chunk in bytes (default: 1MB)
:param compute_checksum: If True, computes and sends MD5 checksum
:raises: FileNotFoundError if file doesn't exist
:raises: IOError if send fails
Example:
# Send a large video file
await protocol.send_file(
"video.mp4",
chunk_size=2 * 1024 * 1024 # 2MB chunks
)
"""
import hashlib
import os
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found: {file_path}")
file_size = os.path.getsize(file_path)
file_name = os.path.basename(file_path)
total_chunks = (file_size + chunk_size - 1) // chunk_size
# Detect MIME type
import mimetypes
import json
mime_type, _ = mimetypes.guess_type(file_path)
# Send file header (as JSON string)
header_msg = {
"type": "file_transfer_start",
"filename": file_name,
"size": file_size,
"chunk_size": chunk_size,
"total_chunks": total_chunks,
"mime_type": mime_type,
}
await self.transport.send(json.dumps(header_msg).encode("utf-8"))
# Send file in chunks
md5_hash = hashlib.md5() if compute_checksum else None
with open(file_path, "rb") as f:
chunk_num = 0
while True:
chunk = f.read(chunk_size)
if not chunk:
break
if md5_hash:
md5_hash.update(chunk)
await self.send_binary_message(
chunk, {"chunk_num": chunk_num, "chunk_size": len(chunk)}
)
chunk_num += 1
self.logger.info(f"Sent chunk {chunk_num}/{total_chunks}")
# Send completion with checksum (as JSON string)
completion_msg = {
"type": "file_transfer_complete",
"filename": file_name,
"total_chunks": chunk_num,
}
if md5_hash:
completion_msg["checksum"] = md5_hash.hexdigest()
await self.transport.send(json.dumps(completion_msg).encode("utf-8"))
self.logger.info(f"File transfer complete: {file_name}")
async def receive_file(
self, output_path: str, validate_checksum: bool = True
) -> Dict[str, Any]:
"""
Receive a file that was sent in chunks.
Receives a chunked file transfer and writes to the specified path.
Validates checksum if provided.
:param output_path: Path where received file should be saved
:param validate_checksum: If True, validates MD5 checksum
:return: Dictionary with transfer metadata (filename, size, checksum, etc.)
:raises: IOError if receive fails
:raises: ValueError if checksum validation fails
Example:
# Receive a file
metadata = await protocol.receive_file("downloads/received_video.mp4")
print(f"Received: {metadata['filename']} ({metadata['size']} bytes)")
"""
import hashlib
import json
import os
# 1. Receive file header
header_bytes = await self.transport.receive()
header = json.loads(header_bytes.decode("utf-8"))
if header.get("type") != "file_transfer_start":
raise ValueError(f"Expected file_transfer_start, got: {header.get('type')}")
filename = header["filename"]
total_size = header["size"]
total_chunks = header["total_chunks"]
self.logger.info(
f"Receiving file: {filename} ({total_size} bytes, {total_chunks} chunks)"
)
# 2. Receive chunks and write to file
md5_hash = hashlib.md5() if validate_checksum else None
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
with open(output_path, "wb") as f:
for chunk_num in range(total_chunks):
data, chunk_meta = await self.receive_binary_message()
if md5_hash:
md5_hash.update(data)
f.write(data)
self.logger.info(f"Received chunk {chunk_num + 1}/{total_chunks}")
# 3. Receive completion message
completion_bytes = await self.transport.receive()
completion = json.loads(completion_bytes.decode("utf-8"))
if completion.get("type") != "file_transfer_complete":
raise ValueError(
f"Expected file_transfer_complete, got: {completion.get('type')}"
)
# 4. Validate checksum
if validate_checksum and "checksum" in completion:
expected_checksum = completion["checksum"]
actual_checksum = md5_hash.hexdigest()
if actual_checksum != expected_checksum:
error_msg = (
f"Checksum mismatch: expected {expected_checksum}, "
f"got {actual_checksum}"
)
self.logger.error(error_msg)
raise ValueError(error_msg)
self.logger.info(f"Checksum validated: {actual_checksum}")
self.logger.info(f"File received successfully: {output_path}")
return {
"filename": filename,
"size": total_size,
"output_path": output_path,
"checksum": completion.get("checksum"),
}
class ProtocolMiddleware(ABC):
"""
Abstract base class for protocol middleware.
Middleware can intercept and modify messages in both directions,
enabling cross-cutting concerns like logging, metrics, and encryption.
"""
@abstractmethod
async def process_outgoing(self, msg: Any) -> Any:
"""
Process outgoing message.
:param msg: Outgoing message
:return: Modified message
"""
pass
@abstractmethod
async def process_incoming(self, msg: Any) -> Any:
"""
Process incoming message.
:param msg: Incoming message
:return: Modified message
"""
pass
class LoggingMiddleware(ProtocolMiddleware):
"""
Middleware that logs all messages.
Useful for debugging and monitoring protocol communication.
"""
def __init__(self, log_level: int = logging.DEBUG):
"""
Initialize logging middleware.
:param log_level: Log level for messages
"""
self.logger = logging.getLogger(f"{__name__}.LoggingMiddleware")
self.log_level = log_level
async def process_outgoing(self, msg: Any) -> Any:
"""Log outgoing message."""
self.logger.log(self.log_level, f"[OUT] {msg}")
return msg
async def process_incoming(self, msg: Any) -> Any:
"""Log incoming message."""
self.logger.log(self.log_level, f"[IN] {msg}")
return msg
================================================
FILE: aip/protocol/command.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Command Protocol
Handles command execution at a fine-grained level.
"""
import logging
from typing import List
from aip.messages import Command, Result
from aip.protocol.base import AIPProtocol
class CommandProtocol(AIPProtocol):
"""
Command execution protocol for AIP.
Provides fine-grained command execution with:
- Typed arguments
- Result validation
- Error propagation
- Batch command support
"""
def __init__(self, *args, **kwargs):
"""Initialize command protocol."""
super().__init__(*args, **kwargs)
self.logger = logging.getLogger(f"{__name__}.CommandProtocol")
def validate_command(self, cmd: Command) -> bool:
"""
Validate a command structure.
:param cmd: Command to validate
:return: True if valid, False otherwise
"""
if not cmd.tool_name:
self.logger.error("Command missing tool_name")
return False
if not cmd.tool_type:
self.logger.error("Command missing tool_type")
return False
return True
def validate_commands(self, commands: List[Command]) -> bool:
"""
Validate a batch of commands.
:param commands: Commands to validate
:return: True if all valid, False otherwise
"""
return all(self.validate_command(cmd) for cmd in commands)
def validate_result(self, result: Result) -> bool:
"""
Validate a command result.
:param result: Result to validate
:return: True if valid, False otherwise
"""
if not result.status:
self.logger.error("Result missing status")
return False
return True
def validate_results(self, results: List[Result]) -> bool:
"""
Validate a batch of results.
:param results: Results to validate
:return: True if all valid, False otherwise
"""
return all(self.validate_result(res) for res in results)
================================================
FILE: aip/protocol/device_info.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Device Info Protocol
Handles device information requests and responses.
"""
import datetime
import logging
from typing import Any, Dict, Optional
from uuid import uuid4
from aip.messages import (
ClientMessage,
ClientMessageType,
ClientType,
ServerMessage,
ServerMessageType,
TaskStatus,
)
from aip.protocol.base import AIPProtocol
class DeviceInfoProtocol(AIPProtocol):
"""
Device information protocol for AIP.
Handles:
- Device info requests from constellation
- Device info responses from device
- System information exchange
"""
def __init__(self, *args, **kwargs):
"""Initialize device info protocol."""
super().__init__(*args, **kwargs)
self.logger = logging.getLogger(f"{__name__}.DeviceInfoProtocol")
async def request_device_info(
self,
constellation_id: str,
target_device: str,
request_id: Optional[str] = None,
) -> None:
"""
Request device information (constellation-side).
:param constellation_id: Constellation client ID
:param target_device: Target device ID
:param request_id: Optional request ID for correlation
"""
req_msg = ClientMessage(
type=ClientMessageType.DEVICE_INFO_REQUEST,
client_type=ClientType.CONSTELLATION,
client_id=constellation_id,
target_id=target_device,
request_id=request_id or str(uuid4()),
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
status=TaskStatus.OK,
)
await self.send_message(req_msg)
self.logger.info(
f"Sent device info request: {constellation_id} → {target_device}"
)
async def send_device_info_response(
self,
device_info: Optional[Dict[str, Any]],
request_id: str,
error: Optional[str] = None,
) -> None:
"""
Send device information response (server-side).
:param device_info: Device information dictionary
:param request_id: Request ID for correlation
:param error: Optional error message
"""
status = TaskStatus.OK if error is None else TaskStatus.ERROR
resp_msg = ServerMessage(
type=ServerMessageType.DEVICE_INFO_RESPONSE,
status=status,
result=device_info,
error=error,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=request_id,
)
await self.send_message(resp_msg)
self.logger.info(f"Sent device info response (request_id: {request_id})")
async def send_device_info_push(
self,
device_id: str,
device_info: Dict[str, Any],
) -> None:
"""
Push device information proactively (device-side, future use).
:param device_id: Device ID
:param device_info: Device information dictionary
"""
push_msg = ClientMessage(
type=ClientMessageType.DEVICE_INFO_RESPONSE,
client_id=device_id,
client_type=ClientType.DEVICE,
metadata=device_info,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
await self.send_message(push_msg)
self.logger.info(f"Pushed device info from {device_id}")
================================================
FILE: aip/protocol/heartbeat.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Heartbeat Protocol
Handles periodic keepalive messages to maintain connection health.
"""
import asyncio
import datetime
import logging
from typing import Optional
from uuid import uuid4
from aip.messages import (
ClientMessage,
ClientMessageType,
ServerMessage,
ServerMessageType,
TaskStatus,
)
from aip.protocol.base import AIPProtocol
class HeartbeatProtocol(AIPProtocol):
"""
Heartbeat protocol for AIP.
Provides:
- Periodic heartbeat messages
- Connection health monitoring
- Automatic heartbeat management
"""
def __init__(self, *args, **kwargs):
"""Initialize heartbeat protocol."""
super().__init__(*args, **kwargs)
self.logger = logging.getLogger(f"{__name__}.HeartbeatProtocol")
self._heartbeat_task: Optional[asyncio.Task] = None
self._heartbeat_interval: float = 30.0 # Default: 30 seconds
async def send_heartbeat(
self, client_id: str, metadata: Optional[dict] = None
) -> None:
"""
Send a single heartbeat message (client-side).
:param client_id: Client ID
:param metadata: Optional metadata dictionary
"""
heartbeat_msg = ClientMessage(
type=ClientMessageType.HEARTBEAT,
client_id=client_id,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
metadata=metadata,
)
await self.send_message(heartbeat_msg)
self.logger.debug(f"Sent heartbeat from {client_id}")
async def send_heartbeat_ack(self, response_id: Optional[str] = None) -> None:
"""
Send heartbeat acknowledgment (server-side).
:param response_id: Optional response ID
"""
ack_msg = ServerMessage(
type=ServerMessageType.HEARTBEAT,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=response_id or str(uuid4()),
)
await self.send_message(ack_msg)
self.logger.debug("Sent heartbeat acknowledgment")
async def start_heartbeat(self, client_id: str, interval: float = 30.0) -> None:
"""
Start automatic heartbeat sending.
:param client_id: Client ID
:param interval: Interval between heartbeats (seconds)
"""
if self._heartbeat_task is not None:
self.logger.warning("Heartbeat already running, stopping existing task")
await self.stop_heartbeat()
self._heartbeat_interval = interval
self._heartbeat_task = asyncio.create_task(
self._heartbeat_loop(client_id, interval)
)
self.logger.info(f"Started heartbeat for {client_id} (interval: {interval}s)")
async def stop_heartbeat(self) -> None:
"""Stop automatic heartbeat sending."""
if self._heartbeat_task is not None:
self._heartbeat_task.cancel()
try:
await self._heartbeat_task
except asyncio.CancelledError:
pass
self._heartbeat_task = None
self.logger.info("Stopped heartbeat")
async def _heartbeat_loop(self, client_id: str, interval: float) -> None:
"""
Internal heartbeat loop.
:param client_id: Client ID
:param interval: Interval between heartbeats (seconds)
"""
try:
while True:
await asyncio.sleep(interval)
if self.is_connected():
await self.send_heartbeat(client_id)
else:
self.logger.warning("Transport not connected, skipping heartbeat")
except asyncio.CancelledError:
self.logger.debug("Heartbeat loop cancelled")
except Exception as e:
self.logger.error(f"Error in heartbeat loop: {e}", exc_info=True)
================================================
FILE: aip/protocol/registration.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Registration Protocol
Handles agent registration and capability advertisement in the AIP system.
"""
import datetime
import logging
from typing import Any, Dict, Optional
from aip.messages import (
ClientMessage,
ClientMessageType,
ClientType,
ServerMessage,
ServerMessageType,
TaskStatus,
)
from aip.protocol.base import AIPProtocol
class RegistrationProtocol(AIPProtocol):
"""
Registration protocol for AIP.
Handles:
- Device agent registration
- Constellation client registration
- Capability advertisement
- Metadata exchange
"""
def __init__(self, *args, **kwargs):
"""Initialize registration protocol."""
super().__init__(*args, **kwargs)
self.logger = logging.getLogger(f"{__name__}.RegistrationProtocol")
async def register_as_device(
self,
device_id: str,
metadata: Optional[Dict[str, Any]] = None,
platform: str = "windows",
) -> bool:
"""
Register as a device agent.
:param device_id: Unique device identifier
:param metadata: Optional device metadata (system info, capabilities, etc.)
:param platform: Platform type (windows, linux, etc.)
:return: True if registration successful, False otherwise
"""
try:
# Prepare metadata
if metadata is None:
metadata = {}
# Add platform to metadata
if "platform" not in metadata:
metadata["platform"] = platform
# Add registration timestamp
metadata["registration_time"] = datetime.datetime.now(
datetime.timezone.utc
).isoformat()
# Create registration message
reg_msg = ClientMessage(
type=ClientMessageType.REGISTER,
client_id=device_id,
client_type=ClientType.DEVICE,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
metadata=metadata,
)
# Send registration
await self.send_message(reg_msg)
self.logger.info(f"Sent device registration for {device_id}")
# Wait for server response
response = await self.receive_message(ServerMessage)
if response.status == TaskStatus.OK:
self.logger.info(f"Device {device_id} registered successfully")
return True
else:
self.logger.error(
f"Device registration failed: {response.error or 'Unknown error'}"
)
return False
except Exception as e:
self.logger.error(f"Error during device registration: {e}", exc_info=True)
return False
async def register_as_constellation(
self,
constellation_id: str,
target_device: str,
metadata: Optional[Dict[str, Any]] = None,
) -> bool:
"""
Register as a constellation client.
:param constellation_id: Unique constellation identifier
:param target_device: Target device ID for this constellation
:param metadata: Optional constellation metadata
:return: True if registration successful, False otherwise
"""
try:
# Prepare metadata
if metadata is None:
metadata = {}
# Add constellation-specific metadata
metadata.update(
{
"type": "constellation_client",
"targeted_device_id": target_device,
"registration_time": datetime.datetime.now(
datetime.timezone.utc
).isoformat(),
}
)
# Create registration message
reg_msg = ClientMessage(
type=ClientMessageType.REGISTER,
client_id=constellation_id,
client_type=ClientType.CONSTELLATION,
target_id=target_device,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
metadata=metadata,
)
# Send registration
await self.send_message(reg_msg)
self.logger.info(
f"Sent constellation registration for {constellation_id} → {target_device}"
)
# Wait for server response
response = await self.receive_message(ServerMessage)
if response.status == TaskStatus.OK:
self.logger.info(
f"Constellation {constellation_id} registered successfully"
)
return True
elif response.status == TaskStatus.ERROR:
self.logger.error(
f"Constellation registration failed: {response.error or 'Unknown error'}"
)
return False
else:
self.logger.warning(
f"Unexpected registration response: {response.status}"
)
return False
except Exception as e:
self.logger.error(
f"Error during constellation registration: {e}", exc_info=True
)
return False
async def send_registration_confirmation(
self, response_id: Optional[str] = None
) -> None:
"""
Send registration confirmation (server-side).
:param response_id: Optional response ID for correlation
"""
confirmation = ServerMessage(
type=ServerMessageType.HEARTBEAT,
status=TaskStatus.OK,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=response_id or self._generate_response_id(),
)
await self.send_message(confirmation)
async def send_registration_error(
self, error: str, response_id: Optional[str] = None
) -> None:
"""
Send registration error (server-side).
:param error: Error message
:param response_id: Optional response ID for correlation
"""
error_msg = ServerMessage(
type=ServerMessageType.ERROR,
status=TaskStatus.ERROR,
error=error,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
response_id=response_id or self._generate_response_id(),
)
await self.send_message(error_msg)
@staticmethod
def _generate_response_id() -> str:
"""Generate a unique response ID."""
import uuid
return str(uuid.uuid4())
================================================
FILE: aip/protocol/task_execution.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Task Execution Protocol
Handles task assignment, execution coordination, and result reporting.
"""
import datetime
import logging
from typing import Any, List, Optional
from uuid import uuid4
from aip.messages import (
ClientMessage,
ClientMessageType,
ClientType,
Command,
Result,
ServerMessage,
ServerMessageType,
TaskStatus,
)
from aip.protocol.base import AIPProtocol
class TaskExecutionProtocol(AIPProtocol):
"""
Task execution protocol for AIP.
Handles:
- Task assignment from constellation to device
- Task status updates
- Command execution
- Result reporting
"""
def __init__(self, *args, **kwargs):
"""Initialize task execution protocol."""
super().__init__(*args, **kwargs)
self.logger = logging.getLogger(f"{__name__}.TaskExecutionProtocol")
async def send_task_request(
self,
request: str,
task_name: str,
session_id: str,
client_id: str,
target_id: Optional[str] = None,
client_type: ClientType = ClientType.DEVICE,
metadata: Optional[dict] = None,
) -> None:
"""
Send a task request.
:param request: Task request text
:param task_name: Task name
:param session_id: Session ID
:param client_id: Client ID
:param target_id: Target device ID (for constellation)
:param client_type: Type of client
:param metadata: Optional metadata
"""
task_msg = ClientMessage(
type=ClientMessageType.TASK,
request=request,
task_name=task_name,
session_id=session_id,
client_id=client_id,
target_id=target_id,
client_type=client_type,
status=TaskStatus.CONTINUE,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
request_id=str(uuid4()),
metadata=metadata,
)
await self.send_message(task_msg)
self.logger.info(f"Sent task request: {task_name}")
async def send_task_assignment(
self,
user_request: str,
task_name: str,
session_id: str,
response_id: str,
agent_name: Optional[str] = None,
process_name: Optional[str] = None,
) -> None:
"""
Send task assignment to device (server-side).
:param user_request: User request text
:param task_name: Task name
:param session_id: Session ID
:param response_id: Response ID
:param agent_name: Agent name
:param process_name: Process name
"""
task_msg = ServerMessage(
type=ServerMessageType.TASK,
status=TaskStatus.CONTINUE,
user_request=user_request,
task_name=task_name,
session_id=session_id,
response_id=response_id,
agent_name=agent_name,
process_name=process_name,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
await self.send_message(task_msg)
self.logger.info(f"Sent task assignment: {task_name}")
async def send_command(self, server_message: ServerMessage) -> None:
"""
Send command(s) to execute (server-side).
Accepts a ServerMessage object directly for backward compatibility.
:param server_message: ServerMessage with commands to execute
"""
await self.send_message(server_message)
actions_count = len(server_message.actions) if server_message.actions else 0
self.logger.info(
f"Sent {actions_count} command(s) for session {server_message.session_id}"
)
async def send_commands(
self,
actions: List[Command],
session_id: str,
response_id: str,
status: TaskStatus = TaskStatus.CONTINUE,
agent_name: Optional[str] = None,
process_name: Optional[str] = None,
root_name: Optional[str] = None,
task_name: Optional[str] = None,
) -> None:
"""
Send command(s) to execute (server-side).
Creates ServerMessage from parameters.
:param actions: List of commands to execute
:param session_id: Session ID
:param response_id: Response ID
:param status: Task status
:param agent_name: Agent name
:param process_name: Process name
:param root_name: Root name
:param task_name: Task name
"""
cmd_msg = ServerMessage(
type=ServerMessageType.COMMAND,
status=status,
actions=actions,
session_id=session_id,
response_id=response_id,
agent_name=agent_name,
process_name=process_name,
root_name=root_name,
task_name=task_name,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
await self.send_message(cmd_msg)
self.logger.info(f"Sent {len(actions)} command(s) for session {session_id}")
async def send_command_results(
self,
action_results: List[Result],
session_id: str,
client_id: str,
prev_response_id: str,
status: TaskStatus = TaskStatus.CONTINUE,
) -> None:
"""
Send command execution results (client-side).
:param action_results: Results of executed commands
:param session_id: Session ID
:param client_id: Client ID
:param prev_response_id: Previous response ID
:param status: Task status
"""
result_msg = ClientMessage(
type=ClientMessageType.COMMAND_RESULTS,
action_results=action_results,
session_id=session_id,
client_id=client_id,
prev_response_id=prev_response_id,
status=status,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
request_id=str(uuid4()),
)
await self.send_message(result_msg)
self.logger.info(
f"Sent {len(action_results)} result(s) for session {session_id}"
)
async def send_task_result(
self,
session_id: str,
prev_response_id: str,
action_results: List[Result],
status: TaskStatus = TaskStatus.CONTINUE,
client_id: Optional[str] = None,
) -> None:
"""
Convenience method to send task results (client-side).
Alias for send_command_results with automatic client_id handling.
:param session_id: Session ID
:param prev_response_id: Previous response ID
:param action_results: Results of executed commands
:param status: Task status
:param client_id: Client ID (optional, will be extracted from context if available)
"""
# If client_id not provided, try to extract from transport or use a default
if not client_id:
client_id = "unknown_client" # Fallback
await self.send_command_results(
action_results=action_results,
session_id=session_id,
client_id=client_id,
prev_response_id=prev_response_id,
status=status,
)
async def send_task_end(
self,
session_id: str,
status: TaskStatus,
result: Optional[Any] = None,
error: Optional[str] = None,
response_id: Optional[str] = None,
) -> None:
"""
Send task completion notification (server-side).
:param session_id: Session ID
:param status: Final task status (COMPLETED or FAILED)
:param result: Task result if successful
:param error: Error message if failed
:param response_id: Response ID
"""
task_end_msg = ServerMessage(
type=ServerMessageType.TASK_END,
status=status,
session_id=session_id,
result=result,
error=error,
response_id=response_id or str(uuid4()),
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
await self.send_message(task_end_msg)
self.logger.info(f"Sent task end for session {session_id}, status: {status}")
async def send_task_end_ack(
self,
session_id: str,
client_id: str,
status: TaskStatus,
error: Optional[str] = None,
) -> None:
"""
Send task end acknowledgment (client-side).
:param session_id: Session ID
:param client_id: Client ID
:param status: Task status
:param error: Error message if failed
"""
task_end_msg = ClientMessage(
type=ClientMessageType.TASK_END,
session_id=session_id,
client_id=client_id,
status=status,
error=error,
timestamp=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
await self.send_message(task_end_msg)
self.logger.info(f"Sent task end ack for session {session_id}")
================================================
FILE: aip/resilience/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Resilience Mechanisms
Provides connection resilience, reconnection strategies, heartbeat management,
and timeout handling for reliable agent communication.
"""
from .heartbeat_manager import HeartbeatManager
from .reconnection import ReconnectionPolicy, ReconnectionStrategy
from .timeout import TimeoutManager
__all__ = [
"ReconnectionStrategy",
"ReconnectionPolicy",
"HeartbeatManager",
"TimeoutManager",
]
================================================
FILE: aip/resilience/heartbeat_manager.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Heartbeat Manager
Manages periodic heartbeat messages to monitor connection health
and detect disconnections early.
"""
import asyncio
import logging
from typing import Dict, Optional
from aip.protocol.heartbeat import HeartbeatProtocol
class HeartbeatManager:
"""
Manages heartbeat for multiple clients/devices.
Features:
- Per-client heartbeat tracking
- Configurable intervals
- Automatic heartbeat sending
- Connection health monitoring
"""
def __init__(
self,
protocol: HeartbeatProtocol,
default_interval: float = 30.0,
):
"""
Initialize heartbeat manager.
:param protocol: Heartbeat protocol instance
:param default_interval: Default interval between heartbeats (seconds)
"""
self.protocol = protocol
self.default_interval = default_interval
self.logger = logging.getLogger(f"{__name__}.HeartbeatManager")
# Track heartbeat tasks per client
self._heartbeat_tasks: Dict[str, asyncio.Task] = {}
self._intervals: Dict[str, float] = {}
async def start_heartbeat(
self, client_id: str, interval: Optional[float] = None
) -> None:
"""
Start heartbeat for a client.
:param client_id: Client ID
:param interval: Heartbeat interval (default: use default_interval)
"""
if client_id in self._heartbeat_tasks:
self.logger.warning(
f"Heartbeat already running for {client_id}, stopping existing"
)
await self.stop_heartbeat(client_id)
interval = interval or self.default_interval
self._intervals[client_id] = interval
# Create heartbeat task
task = asyncio.create_task(self._heartbeat_loop(client_id, interval))
self._heartbeat_tasks[client_id] = task
self.logger.info(f"Started heartbeat for {client_id} (interval: {interval}s)")
async def stop_heartbeat(self, client_id: str) -> None:
"""
Stop heartbeat for a client.
:param client_id: Client ID
"""
task = self._heartbeat_tasks.pop(client_id, None)
if task:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
self._intervals.pop(client_id, None)
self.logger.info(f"Stopped heartbeat for {client_id}")
async def stop_all(self) -> None:
"""Stop all heartbeats."""
client_ids = list(self._heartbeat_tasks.keys())
for client_id in client_ids:
await self.stop_heartbeat(client_id)
self.logger.info("Stopped all heartbeats")
def is_running(self, client_id: str) -> bool:
"""
Check if heartbeat is running for a client.
:param client_id: Client ID
:return: True if running, False otherwise
"""
task = self._heartbeat_tasks.get(client_id)
return task is not None and not task.done()
def get_interval(self, client_id: str) -> Optional[float]:
"""
Get heartbeat interval for a client.
:param client_id: Client ID
:return: Interval in seconds, or None if not running
"""
return self._intervals.get(client_id)
async def _heartbeat_loop(self, client_id: str, interval: float) -> None:
"""
Internal heartbeat loop for a client.
:param client_id: Client ID
:param interval: Heartbeat interval (seconds)
"""
try:
while True:
await asyncio.sleep(interval)
# Check if protocol is still connected
if self.protocol.is_connected():
try:
await self.protocol.send_heartbeat(client_id)
self.logger.debug(f"Sent heartbeat for {client_id}")
except Exception as e:
self.logger.error(
f"Error sending heartbeat for {client_id}: {e}"
)
# Let the loop continue, connection manager will handle disconnection
else:
self.logger.warning(
f"Protocol not connected for {client_id}, skipping heartbeat"
)
except asyncio.CancelledError:
self.logger.debug(f"Heartbeat loop cancelled for {client_id}")
except Exception as e:
self.logger.error(
f"Unexpected error in heartbeat loop for {client_id}: {e}",
exc_info=True,
)
================================================
FILE: aip/resilience/reconnection.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Reconnection Strategy
Implements automatic reconnection with exponential backoff for handling
transient network failures and connection interruptions.
"""
import asyncio
import logging
from enum import Enum
from typing import TYPE_CHECKING, Awaitable, Callable, Optional
if TYPE_CHECKING:
from aip.endpoints.base import AIPEndpoint
class ReconnectionPolicy(str, Enum):
"""Reconnection policies."""
EXPONENTIAL_BACKOFF = "exponential_backoff"
LINEAR_BACKOFF = "linear_backoff"
IMMEDIATE = "immediate"
NONE = "none"
class ReconnectionStrategy:
"""
Manages automatic reconnection for AIP endpoints.
Features:
- Exponential backoff
- Configurable retry limits
- Connection state callbacks
- Task cancellation on disconnect
"""
def __init__(
self,
max_retries: int = 5,
initial_backoff: float = 1.0,
max_backoff: float = 60.0,
backoff_multiplier: float = 2.0,
policy: ReconnectionPolicy = ReconnectionPolicy.EXPONENTIAL_BACKOFF,
):
"""
Initialize reconnection strategy.
:param max_retries: Maximum number of reconnection attempts
:param initial_backoff: Initial backoff time (seconds)
:param max_backoff: Maximum backoff time (seconds)
:param backoff_multiplier: Multiplier for exponential backoff
:param policy: Reconnection policy
"""
self.max_retries = max_retries
self.initial_backoff = initial_backoff
self.max_backoff = max_backoff
self.backoff_multiplier = backoff_multiplier
self.policy = policy
self.logger = logging.getLogger(f"{__name__}.ReconnectionStrategy")
self._retry_count = 0
self._reconnection_task: Optional[asyncio.Task] = None
async def handle_disconnection(
self,
endpoint: "AIPEndpoint",
device_id: str,
on_reconnect: Optional[Callable[[], Awaitable[None]]] = None,
) -> None:
"""
Handle device disconnection with automatic reconnection.
Workflow:
1. Cancel all pending tasks for the device
2. Notify upper layers of disconnection
3. Attempt reconnection with backoff
4. Call on_reconnect callback if successful
:param endpoint: AIP endpoint managing the connection
:param device_id: Device that disconnected
:param on_reconnect: Optional callback after successful reconnection
"""
self.logger.warning(f"Device {device_id} disconnected, starting recovery")
# Step 1: Cancel pending tasks
await self._cancel_pending_tasks(endpoint, device_id)
# Step 2: Notify upper layers
await self._notify_disconnection(endpoint, device_id)
# Step 3: Attempt reconnection
if self.policy != ReconnectionPolicy.NONE:
reconnected = await self.attempt_reconnection(endpoint, device_id)
# Step 4: Call reconnection callback
if reconnected and on_reconnect:
try:
await on_reconnect()
self.logger.info(f"Reconnection callback executed for {device_id}")
except Exception as e:
self.logger.error(
f"Error in reconnection callback for {device_id}: {e}"
)
async def attempt_reconnection(
self, endpoint: "AIPEndpoint", device_id: str
) -> bool:
"""
Attempt to reconnect to a device.
:param endpoint: AIP endpoint managing the connection
:param device_id: Device to reconnect to
:return: True if reconnection successful, False otherwise
"""
self._retry_count = 0
while self._retry_count < self.max_retries:
# Calculate backoff time
backoff_time = self._calculate_backoff()
self.logger.info(
f"Reconnection attempt {self._retry_count + 1}/{self.max_retries} "
f"for {device_id} in {backoff_time:.1f}s"
)
# Wait before attempting reconnection
await asyncio.sleep(backoff_time)
# Try to reconnect
try:
success = await endpoint.reconnect_device(device_id)
if success:
self.logger.info(
f"Successfully reconnected to {device_id} "
f"after {self._retry_count + 1} attempt(s)"
)
self._retry_count = 0
return True
else:
self.logger.warning(
f"Reconnection attempt {self._retry_count + 1} failed for {device_id}"
)
except Exception as e:
self.logger.error(
f"Error during reconnection attempt {self._retry_count + 1} for {device_id}: {e}"
)
self._retry_count += 1
self.logger.error(
f"Max reconnection attempts ({self.max_retries}) reached for {device_id}"
)
return False
async def _cancel_pending_tasks(
self, endpoint: "AIPEndpoint", device_id: str
) -> None:
"""
Cancel all pending tasks for a disconnected device.
:param endpoint: AIP endpoint
:param device_id: Disconnected device ID
"""
try:
if hasattr(endpoint, "cancel_device_tasks"):
await endpoint.cancel_device_tasks(
device_id, reason="device_disconnected"
)
self.logger.info(f"Cancelled pending tasks for {device_id}")
except Exception as e:
self.logger.error(
f"Error cancelling tasks for {device_id}: {e}", exc_info=True
)
async def _notify_disconnection(
self, endpoint: "AIPEndpoint", device_id: str
) -> None:
"""
Notify upper layers of device disconnection.
:param endpoint: AIP endpoint
:param device_id: Disconnected device ID
"""
try:
if hasattr(endpoint, "on_device_disconnected"):
await endpoint.on_device_disconnected(device_id)
self.logger.info(f"Notified disconnection of {device_id}")
except Exception as e:
self.logger.error(
f"Error notifying disconnection for {device_id}: {e}", exc_info=True
)
def _calculate_backoff(self) -> float:
"""
Calculate backoff time based on policy.
:return: Backoff time in seconds
"""
if self.policy == ReconnectionPolicy.IMMEDIATE:
return 0.0
elif self.policy == ReconnectionPolicy.LINEAR_BACKOFF:
backoff = self.initial_backoff * (self._retry_count + 1)
elif self.policy == ReconnectionPolicy.EXPONENTIAL_BACKOFF:
backoff = self.initial_backoff * (
self.backoff_multiplier**self._retry_count
)
else:
return 0.0
# Cap at max_backoff
return min(backoff, self.max_backoff)
def reset(self) -> None:
"""Reset retry counter."""
self._retry_count = 0
================================================
FILE: aip/resilience/timeout.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Timeout Manager
Handles timeout enforcement for asynchronous operations in AIP.
"""
import asyncio
import logging
from typing import Any, Awaitable, Optional, TypeVar
T = TypeVar("T")
class TimeoutManager:
"""
Manages timeouts for asynchronous operations.
Features:
- Configurable default timeout
- Per-operation timeout override
- Timeout exception wrapping
- Detailed logging
"""
def __init__(self, default_timeout: float = 120.0):
"""
Initialize timeout manager.
:param default_timeout: Default timeout for operations (seconds)
"""
self.default_timeout = default_timeout
self.logger = logging.getLogger(f"{__name__}.TimeoutManager")
async def with_timeout(
self,
coro: Awaitable[T],
timeout: Optional[float] = None,
operation_name: str = "operation",
) -> T:
"""
Execute a coroutine with timeout.
:param coro: Coroutine to execute
:param timeout: Timeout in seconds (default: use default_timeout)
:param operation_name: Name of operation for logging
:return: Result of coroutine
:raises: asyncio.TimeoutError if operation times out
"""
timeout = timeout or self.default_timeout
try:
self.logger.debug(f"Starting {operation_name} with timeout {timeout}s")
result = await asyncio.wait_for(coro, timeout=timeout)
self.logger.debug(f"Completed {operation_name}")
return result
except asyncio.TimeoutError:
self.logger.error(f"Timeout ({timeout}s) exceeded for {operation_name}")
raise asyncio.TimeoutError(f"{operation_name} timed out after {timeout}s")
except Exception as e:
self.logger.error(f"Error in {operation_name}: {e}", exc_info=True)
raise
async def with_timeout_or_none(
self,
coro: Awaitable[T],
timeout: Optional[float] = None,
operation_name: str = "operation",
) -> Optional[T]:
"""
Execute a coroutine with timeout, returning None on timeout.
:param coro: Coroutine to execute
:param timeout: Timeout in seconds (default: use default_timeout)
:param operation_name: Name of operation for logging
:return: Result of coroutine or None if timeout
"""
try:
return await self.with_timeout(coro, timeout, operation_name)
except asyncio.TimeoutError:
self.logger.warning(f"{operation_name} timed out, returning None")
return None
================================================
FILE: aip/transport/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
AIP Transport Layer
Provides transport abstractions for the Agent Interaction Protocol.
Supports WebSocket and is extensible to other transports (HTTP/3, gRPC, etc.).
"""
from .adapters import (
FastAPIWebSocketAdapter,
WebSocketAdapter,
WebSocketsLibAdapter,
create_adapter,
)
from .base import Transport, TransportState
from .websocket import WebSocketTransport
__all__ = [
"Transport",
"TransportState",
"WebSocketTransport",
"WebSocketAdapter",
"FastAPIWebSocketAdapter",
"WebSocketsLibAdapter",
"create_adapter",
]
================================================
FILE: aip/transport/adapters.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
WebSocket Adapter Interface
Provides a unified interface for different WebSocket implementations.
Uses the Adapter pattern to abstract away differences between:
- FastAPI WebSocket (server-side)
- websockets library (client-side)
Supports both text and binary frame transmission for efficient file transfer.
"""
from abc import ABC, abstractmethod
from typing import Union
from websockets import WebSocketClientProtocol
class WebSocketAdapter(ABC):
"""
Abstract adapter for WebSocket operations.
Provides a consistent interface regardless of the underlying WebSocket implementation.
Supports both text frames (for JSON messages) and binary frames (for file transfer).
"""
@abstractmethod
async def send(self, data: str) -> None:
"""
Send text data through WebSocket.
:param data: Text data to send
:raises: Exception if send fails
"""
pass
@abstractmethod
async def receive(self) -> str:
"""
Receive text data from WebSocket.
:return: Received text data
:raises: Exception if receive fails
"""
pass
@abstractmethod
async def send_bytes(self, data: bytes) -> None:
"""
Send binary data through WebSocket.
Sends data as a binary WebSocket frame for efficient transmission
of images, files, and other binary content.
:param data: Binary data to send
:raises: Exception if send fails
"""
pass
@abstractmethod
async def receive_bytes(self) -> bytes:
"""
Receive binary data from WebSocket.
Expects a binary WebSocket frame. Raises an error if a text frame is received.
:return: Received binary data
:raises: ValueError if a text frame is received instead of binary
:raises: Exception if receive fails
"""
pass
@abstractmethod
async def receive_auto(self) -> Union[str, bytes]:
"""
Receive data and auto-detect frame type (text or binary).
This method automatically detects whether the received WebSocket frame
is text or binary and returns the appropriate type.
:return: Received data (str for text frames, bytes for binary frames)
:raises: Exception if receive fails
"""
pass
@abstractmethod
async def close(self) -> None:
"""
Close the WebSocket connection.
"""
pass
@abstractmethod
def is_open(self) -> bool:
"""
Check if the WebSocket connection is open.
:return: True if connection is open, False otherwise
"""
pass
class FastAPIWebSocketAdapter(WebSocketAdapter):
"""
Adapter for FastAPI/Starlette WebSocket (server-side).
Used when the server accepts WebSocket connections from clients.
Supports both text and binary frame transmission.
"""
def __init__(self, websocket):
"""
Initialize FastAPI WebSocket adapter.
:param websocket: FastAPI WebSocket instance
"""
from fastapi import WebSocket
self._ws: WebSocket = websocket
async def send(self, data: str) -> None:
"""Send text data via FastAPI WebSocket."""
await self._ws.send_text(data)
async def receive(self) -> str:
"""Receive text data via FastAPI WebSocket."""
return await self._ws.receive_text()
async def send_bytes(self, data: bytes) -> None:
"""
Send binary data via FastAPI WebSocket.
FastAPI provides native send_bytes() method for binary frames.
"""
await self._ws.send_bytes(data)
async def receive_bytes(self) -> bytes:
"""
Receive binary data via FastAPI WebSocket.
FastAPI provides native receive_bytes() method.
Raises an error if a text frame is received.
"""
return await self._ws.receive_bytes()
async def receive_auto(self) -> Union[str, bytes]:
"""
Auto-detect and receive text or binary data.
Uses FastAPI's receive() to get the raw message and extract
the appropriate data type.
"""
message = await self._ws.receive()
if "text" in message:
return message["text"]
elif "bytes" in message:
return message["bytes"]
else:
raise ValueError(f"Unknown WebSocket message type: {message}")
async def close(self) -> None:
"""Close FastAPI WebSocket connection."""
await self._ws.close()
def is_open(self) -> bool:
"""Check if FastAPI WebSocket is still connected."""
from starlette.websockets import WebSocketState
return self._ws.client_state == WebSocketState.CONNECTED
class WebSocketsLibAdapter(WebSocketAdapter):
"""
Adapter for websockets library (client-side).
Used when the client connects to a WebSocket server.
Supports both text and binary frame transmission.
"""
def __init__(self, websocket: WebSocketClientProtocol):
"""
Initialize websockets library adapter.
:param websocket: websockets library WebSocket instance
"""
self._ws: WebSocketClientProtocol = websocket
async def send(self, data: str) -> None:
"""Send text data via websockets library."""
await self._ws.send(data)
async def receive(self) -> str:
"""Receive data via websockets library (handles both text and bytes)."""
received = await self._ws.recv()
# websockets library can return either str or bytes
if isinstance(received, bytes):
return received.decode("utf-8")
return received
async def send_bytes(self, data: bytes) -> None:
"""
Send binary data via websockets library.
The websockets library automatically detects bytes type and sends
as a binary WebSocket frame.
"""
await self._ws.send(data)
async def receive_bytes(self) -> bytes:
"""
Receive binary data via websockets library.
Raises ValueError if a text frame is received instead of binary.
"""
received = await self._ws.recv()
if isinstance(received, str):
raise ValueError(
"Expected binary WebSocket frame, but received text frame. "
f"Received data: {received[:100]}..."
)
return received
async def receive_auto(self) -> Union[str, bytes]:
"""
Auto-detect and receive text or binary data.
The websockets library's recv() automatically returns the correct type
(str for text frames, bytes for binary frames).
"""
return await self._ws.recv()
async def close(self) -> None:
"""Close websockets library connection."""
await self._ws.close()
def is_open(self) -> bool:
"""Check if websockets library connection is still open."""
return not self._ws.closed
def create_adapter(websocket) -> WebSocketAdapter:
"""
Factory function to create the appropriate WebSocket adapter.
Auto-detects the WebSocket type and returns the correct adapter.
:param websocket: Either FastAPI WebSocket or websockets library WebSocket
:return: Appropriate adapter instance
"""
# Check if it's a FastAPI WebSocket by looking for server-side attributes
if hasattr(websocket, "client_state") or hasattr(websocket, "application_state"):
return FastAPIWebSocketAdapter(websocket)
else:
return WebSocketsLibAdapter(websocket)
================================================
FILE: aip/transport/base.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Base Transport Interface
Defines the abstract interface for all AIP transports.
This allows AIP to work with different underlying communication mechanisms
while maintaining a consistent protocol layer.
"""
from abc import ABC, abstractmethod
from enum import Enum
class TransportState(str, Enum):
"""
State of a transport connection.
DISCONNECTED: Not connected
CONNECTING: Connection in progress
CONNECTED: Active connection
DISCONNECTING: Graceful shutdown in progress
ERROR: Transport error occurred
"""
DISCONNECTED = "disconnected"
CONNECTING = "connecting"
CONNECTED = "connected"
DISCONNECTING = "disconnecting"
ERROR = "error"
class Transport(ABC):
"""
Abstract base class for AIP transports.
A transport handles the low-level sending and receiving of messages
between AIP endpoints. It abstracts away the specifics of the
underlying communication channel (WebSocket, HTTP, gRPC, etc.).
Implementations must be:
- Asynchronous (use async/await)
- Thread-safe for state queries
- Resilient to transient errors
"""
def __init__(self):
"""Initialize transport."""
self._state: TransportState = TransportState.DISCONNECTED
@property
def state(self) -> TransportState:
"""Get current transport state."""
return self._state
@property
def is_connected(self) -> bool:
"""Check if transport is connected."""
return self._state == TransportState.CONNECTED
@abstractmethod
async def connect(self, url: str, **kwargs) -> None:
"""
Establish connection to the remote endpoint.
:param url: Target URL/address
:param kwargs: Transport-specific connection parameters
:raises: ConnectionError if connection fails
"""
pass
@abstractmethod
async def send(self, data: bytes) -> None:
"""
Send data through the transport.
:param data: Bytes to send
:raises: ConnectionError if not connected
:raises: IOError if send fails
"""
pass
@abstractmethod
async def receive(self) -> bytes:
"""
Receive data from the transport.
Blocks until data is available.
:return: Received bytes
:raises: ConnectionError if connection closed
:raises: IOError if receive fails
"""
pass
@abstractmethod
async def close(self) -> None:
"""
Close the transport connection.
Should be idempotent (safe to call multiple times).
"""
pass
@abstractmethod
async def wait_closed(self) -> None:
"""
Wait for transport to fully close.
Useful for graceful shutdown.
"""
pass
def __repr__(self) -> str:
"""String representation of transport."""
return f"{self.__class__.__name__}(state={self.state})"
================================================
FILE: aip/transport/websocket.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
WebSocket Transport Implementation
Implements the Transport interface using WebSockets.
Provides reliable, bidirectional, full-duplex communication over a single TCP connection.
Supports both text frames (for JSON messages) and binary frames (for efficient file transfer).
"""
import asyncio
import logging
from typing import Optional, Union
import websockets
from websockets import WebSocketClientProtocol
from websockets.exceptions import ConnectionClosed, WebSocketException
from .adapters import WebSocketAdapter, create_adapter
from .base import Transport, TransportState
class WebSocketTransport(Transport):
"""
WebSocket-based transport for AIP.
Features:
- Automatic ping/pong keepalive
- Configurable timeouts
- Large message support (up to 100MB by default)
- Graceful connection shutdown
- Text and binary frame support for efficient data transfer
Usage:
# Text messages (JSON)
transport = WebSocketTransport(ping_interval=30, ping_timeout=180)
await transport.connect("ws://localhost:8000/ws")
await transport.send(b"Hello")
data = await transport.receive()
# Binary data (files, images)
await transport.send_binary(image_bytes)
binary_data = await transport.receive_binary()
# Auto-detect frame type
data = await transport.receive_auto() # Returns str or bytes
await transport.close()
"""
def __init__(
self,
websocket=None, # Accept existing WebSocket (FastAPI server-side)
ping_interval: float = 30.0,
ping_timeout: float = 180.0,
close_timeout: float = 10.0,
max_size: int = 100 * 1024 * 1024, # 100MB
):
"""
Initialize WebSocket transport.
:param websocket: Optional existing WebSocket connection (for server-side use)
:param ping_interval: Interval between ping messages (seconds)
:param ping_timeout: Timeout for ping response (seconds)
:param close_timeout: Timeout for graceful close (seconds)
:param max_size: Maximum message size in bytes
"""
super().__init__()
self.ping_interval = ping_interval
self.ping_timeout = ping_timeout
self.close_timeout = close_timeout
self.max_size = max_size
self._ws: Optional[WebSocketClientProtocol] = None
self._adapter: Optional[WebSocketAdapter] = None
self.logger = logging.getLogger(f"{__name__}.WebSocketTransport")
# If websocket provided (server-side), create adapter and mark as connected
if websocket is not None:
self._ws = websocket
self._adapter = create_adapter(websocket)
self._state = TransportState.CONNECTED
adapter_type = type(self._adapter).__name__
self.logger.info(
f"WebSocket transport initialized with existing connection ({adapter_type})"
)
async def connect(self, url: str, **kwargs) -> None:
"""
Connect to WebSocket server.
:param url: WebSocket URL (ws:// or wss://)
:param kwargs: Additional parameters passed to websockets.connect()
:raises: ConnectionError if connection fails
"""
if self._state == TransportState.CONNECTED:
self.logger.warning("Already connected, disconnecting first")
await self.close()
try:
self._state = TransportState.CONNECTING
self.logger.info(f"Connecting to {url}")
# Merge user kwargs with defaults
connect_params = {
"ping_interval": self.ping_interval,
"ping_timeout": self.ping_timeout,
"close_timeout": self.close_timeout,
"max_size": self.max_size,
}
connect_params.update(kwargs)
self._ws = await websockets.connect(url, **connect_params)
self._adapter = create_adapter(self._ws)
self._state = TransportState.CONNECTED
self.logger.info(f"Connected to {url}")
except WebSocketException as e:
self._state = TransportState.ERROR
self.logger.error(f"WebSocket error during connection: {e}")
raise ConnectionError(f"Failed to connect to {url}: {e}") from e
except OSError as e:
self._state = TransportState.ERROR
self.logger.error(f"Network error during connection: {e}")
raise ConnectionError(f"Network error connecting to {url}: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Unexpected error during connection: {e}")
raise ConnectionError(f"Unexpected error connecting to {url}: {e}") from e
async def send(self, data: bytes) -> None:
"""
Send data through WebSocket.
:param data: Bytes to send
:raises: ConnectionError if not connected
:raises: IOError if send fails
"""
if not self.is_connected or self._adapter is None:
raise ConnectionError("Transport not connected")
# Check if WebSocket is still open using adapter
if not self._adapter.is_open():
self._state = TransportState.DISCONNECTED
raise ConnectionError("WebSocket connection is closed")
try:
# Convert bytes to text for consistent transport (JSON messages are text-based)
text_data = data.decode("utf-8") if isinstance(data, bytes) else data
adapter_type = type(self._adapter).__name__
self.logger.debug(f"Sending {len(text_data)} chars via {adapter_type}")
# Use adapter to send (abstracts away FastAPI vs websockets library)
await self._adapter.send(text_data)
self.logger.debug(f"✅ Sent {len(text_data)} chars successfully")
except ConnectionClosed as e:
self._state = TransportState.DISCONNECTED
self.logger.debug(f"Connection closed during send: {e}")
raise ConnectionError(f"Connection closed: {e}") from e
except (ConnectionError, OSError) as e:
self._state = TransportState.ERROR
# Check if this is a normal disconnection scenario
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot send (connection closed): {e}")
else:
self.logger.warning(f"Connection error sending data: {e}")
raise IOError(f"Failed to send data: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Error sending data: {e}")
raise IOError(f"Failed to send data: {e}") from e
async def receive(self) -> bytes:
"""
Receive data from WebSocket.
Blocks until data is available.
:return: Received bytes
:raises: ConnectionError if connection closed
:raises: IOError if receive fails
"""
if not self.is_connected or self._adapter is None:
raise ConnectionError("Transport not connected")
try:
adapter_type = type(self._adapter).__name__
self.logger.debug(f"🔍 Attempting to receive data via {adapter_type}...")
# Use adapter to receive (abstracts away FastAPI vs websockets library)
text_data = await self._adapter.receive()
data = text_data.encode("utf-8")
self.logger.debug(f"✅ Received {len(data)} bytes successfully")
return data
except ConnectionClosed as e:
self._state = TransportState.DISCONNECTED
self.logger.debug(f"Connection closed during receive: {e}")
raise ConnectionError(f"Connection closed: {e}") from e
except (ConnectionError, OSError) as e:
self._state = TransportState.ERROR
# Check if this is a normal disconnection scenario
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot receive (connection closed): {e}")
else:
self.logger.warning(f"Connection error receiving data: {e}")
raise IOError(f"Failed to receive data: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Error receiving data: {e}")
raise IOError(f"Failed to receive data: {e}") from e
async def close(self) -> None:
"""
Close WebSocket connection gracefully.
Idempotent - safe to call multiple times.
"""
if self._state in (TransportState.DISCONNECTED, TransportState.DISCONNECTING):
return
try:
self._state = TransportState.DISCONNECTING
if self._adapter is not None:
await self._adapter.close()
self.logger.info("WebSocket closed")
except Exception as e:
self.logger.warning(f"Error during close: {e}")
finally:
self._state = TransportState.DISCONNECTED
self._ws = None
self._adapter = None
async def wait_closed(self) -> None:
"""
Wait for WebSocket to fully close.
Useful for graceful shutdown.
"""
if self._ws is not None:
await self._ws.wait_closed()
self._state = TransportState.DISCONNECTED
async def send_binary(self, data: bytes) -> None:
"""
Send binary data through WebSocket as a binary frame.
This method sends raw binary data (images, files, etc.) without
text encoding overhead, providing maximum efficiency for binary transfers.
:param data: Binary bytes to send
:raises: ConnectionError if not connected
:raises: IOError if send fails
Example:
# Send an image file
with open("screenshot.png", "rb") as f:
image_data = f.read()
await transport.send_binary(image_data)
"""
if not self.is_connected or self._adapter is None:
raise ConnectionError("Transport not connected")
if not self._adapter.is_open():
self._state = TransportState.DISCONNECTED
raise ConnectionError("WebSocket connection is closed")
try:
adapter_type = type(self._adapter).__name__
self.logger.debug(
f"Sending {len(data)} bytes (binary frame) via {adapter_type}"
)
await self._adapter.send_bytes(data)
self.logger.debug(f"✅ Sent {len(data)} bytes successfully")
except ConnectionClosed as e:
self._state = TransportState.DISCONNECTED
self.logger.debug(f"Connection closed during binary send: {e}")
raise ConnectionError(f"Connection closed: {e}") from e
except (ConnectionError, OSError) as e:
self._state = TransportState.ERROR
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot send binary (connection closed): {e}")
else:
self.logger.warning(f"Connection error sending binary data: {e}")
raise IOError(f"Failed to send binary data: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Error sending binary data: {e}")
raise IOError(f"Failed to send binary data: {e}") from e
async def receive_binary(self) -> bytes:
"""
Receive binary data from WebSocket as a binary frame.
This method expects a binary WebSocket frame and returns raw bytes.
Raises an error if a text frame is received.
:return: Received binary bytes
:raises: ConnectionError if connection closed
:raises: ValueError if a text frame is received instead of binary
:raises: IOError if receive fails
Example:
# Receive a binary file
file_data = await transport.receive_binary()
with open("received_file.bin", "wb") as f:
f.write(file_data)
"""
if not self.is_connected or self._adapter is None:
raise ConnectionError("Transport not connected")
try:
adapter_type = type(self._adapter).__name__
self.logger.debug(
f"🔍 Attempting to receive binary data via {adapter_type}..."
)
data = await self._adapter.receive_bytes()
self.logger.debug(f"✅ Received {len(data)} bytes successfully")
return data
except ConnectionClosed as e:
self._state = TransportState.DISCONNECTED
self.logger.debug(f"Connection closed during binary receive: {e}")
raise ConnectionError(f"Connection closed: {e}") from e
except ValueError as e:
# Raised when expecting binary but got text frame
self.logger.error(f"Frame type mismatch: {e}")
raise
except (ConnectionError, OSError) as e:
self._state = TransportState.ERROR
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot receive binary (connection closed): {e}")
else:
self.logger.warning(f"Connection error receiving binary data: {e}")
raise IOError(f"Failed to receive binary data: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Error receiving binary data: {e}")
raise IOError(f"Failed to receive binary data: {e}") from e
async def receive_auto(self) -> Union[bytes, str]:
"""
Receive data and automatically detect frame type (text or binary).
This method receives a WebSocket frame and returns the appropriate type:
- str for text frames (JSON messages)
- bytes for binary frames (files, images)
:return: Received data (str for text frames, bytes for binary frames)
:raises: ConnectionError if connection closed
:raises: IOError if receive fails
Example:
data = await transport.receive_auto()
if isinstance(data, bytes):
# Handle binary data
print(f"Received {len(data)} bytes")
else:
# Handle text data
message = json.loads(data)
"""
if not self.is_connected or self._adapter is None:
raise ConnectionError("Transport not connected")
try:
adapter_type = type(self._adapter).__name__
self.logger.debug(
f"🔍 Attempting to receive data (auto-detect) via {adapter_type}..."
)
data = await self._adapter.receive_auto()
if isinstance(data, bytes):
self.logger.debug(
f"✅ Received {len(data)} bytes (binary frame) successfully"
)
else:
self.logger.debug(
f"✅ Received {len(data)} chars (text frame) successfully"
)
return data
except ConnectionClosed as e:
self._state = TransportState.DISCONNECTED
self.logger.debug(f"Connection closed during receive: {e}")
raise ConnectionError(f"Connection closed: {e}") from e
except (ConnectionError, OSError) as e:
self._state = TransportState.ERROR
error_msg = str(e).lower()
if "closed" in error_msg or "not connected" in error_msg:
self.logger.debug(f"Cannot receive (connection closed): {e}")
else:
self.logger.warning(f"Connection error receiving data: {e}")
raise IOError(f"Failed to receive data: {e}") from e
except Exception as e:
self._state = TransportState.ERROR
self.logger.error(f"Error receiving data: {e}")
raise IOError(f"Failed to receive data: {e}") from e
@property
def websocket(self) -> Optional[WebSocketClientProtocol]:
"""
Get the underlying WebSocket connection.
:return: WebSocket connection or None if not connected
"""
return self._ws
def __repr__(self) -> str:
"""String representation."""
return f"WebSocketTransport(state={self.state}, ping_interval={self.ping_interval})"
================================================
FILE: config/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
UFO² Configuration System
Modern, modular configuration system with type safety and backward compatibility.
"""
from config.config_loader import (
ConfigLoader,
get_ufo_config,
get_galaxy_config,
clear_config_cache,
)
from config.config_schemas import (
UFOConfig,
GalaxyConfig,
AgentConfig,
SystemConfig,
RAGConfig,
)
__all__ = [
"ConfigLoader",
"get_ufo_config",
"get_galaxy_config",
"clear_config_cache",
"UFOConfig",
"GalaxyConfig",
"AgentConfig",
"SystemConfig",
"RAGConfig",
]
================================================
FILE: config/config_loader.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
"""
Modern Configuration Loader for UFO³ and Galaxy
Professional Software Engineering Design:
- ✅ Separation of Concerns: Modular YAML files for different config domains
- ✅ Backward Compatibility: Automatic fallback to legacy paths (ufo/config/)
- ✅ Migration Support: Built-in migration warnings and tools
- ✅ Type Safety: Pydantic-style typed configs + dynamic YAML fields
- ✅ Auto-Discovery: Loads all YAML files automatically
- ✅ Environment Overrides: dev/test/prod environment support
- ✅ Priority Chain: New path → Legacy path → Environment variables
- ✅ Zero Breaking Changes: Existing code continues to work
Configuration Structure:
New (Recommended):
config/ufo/ ← UFO² configurations
config/galaxy/ ← Galaxy configurations
Legacy (Auto-detected):
ufo/config/ ← Old UFO configs (still supported)
Priority Rules:
1. config/{module}/ ← Highest priority (new path)
2. {module}/config/ ← Fallback (legacy path)
3. Environment vars ← Override mechanism
Usage Examples:
# Load config (automatic fallback to legacy)
config = get_ufo_config()
# Type-safe access (IDE autocomplete!)
max_step = config.system.max_step
api_model = config.app_agent.api_model
# Dynamic YAML fields (no code changes needed!)
new_field = config.NEW_FEATURE
setting = config["CUSTOM_SETTING"]
# Backward compatible
old_style = config["MAX_STEP"] # Still works!
"""
import logging
import os
import re
from pathlib import Path
from typing import Any, Dict, List, Optional
import yaml
from config.config_schemas import UFOConfig, GalaxyConfig
logger = logging.getLogger(__name__)
class DynamicConfig:
"""
Dynamic configuration object that provides both dict-like and attribute access.
Usage:
config = DynamicConfig(data)
# Dict-style access (backward compatible)
value = config["MAX_STEP"]
# Attribute-style access (modern)
value = config.MAX_STEP
# Nested access
value = config.HOST_AGENT.API_MODEL
"""
def __init__(self, data: Dict[str, Any], name: str = "config"):
"""
Initialize DynamicConfig.
:param data: Configuration data dictionary
:param name: Name of this configuration (for debugging)
"""
self._data = data
self._name = name
self._nested_configs = {}
# Pre-create nested configs for dict values
for key, value in data.items():
if isinstance(value, dict):
self._nested_configs[key] = DynamicConfig(value, name=key)
def __getattr__(self, name: str) -> Any:
"""Attribute-style access: config.MAX_STEP"""
if name.startswith("_"):
return object.__getattribute__(self, name)
# Check if we have a pre-created nested config
if name in self._nested_configs:
return self._nested_configs[name]
# Return value from data
if name in self._data:
value = self._data[name]
if isinstance(value, dict):
# Create nested config on-the-fly
nested = DynamicConfig(value, name=name)
self._nested_configs[name] = nested
return nested
return value
raise AttributeError(f"'{self._name}' configuration has no attribute '{name}'")
def __getitem__(self, key: str) -> Any:
"""Dict-style access: config["MAX_STEP"]"""
if key in self._nested_configs:
return self._nested_configs[key]
return self._data[key]
def __contains__(self, key: str) -> bool:
"""Support 'in' operator"""
return key in self._data
def get(self, key: str, default: Any = None) -> Any:
"""Dict-style get with default"""
if key in self._nested_configs:
return self._nested_configs[key]
return self._data.get(key, default)
def keys(self) -> List[str]:
"""Get all keys"""
return self._data.keys()
def items(self):
"""Get all items"""
return self._data.items()
def values(self):
"""Get all values"""
return self._data.values()
def to_dict(self) -> Dict[str, Any]:
"""Convert to plain dictionary"""
return self._data.copy()
def __repr__(self) -> str:
return f"DynamicConfig({self._name})"
def __str__(self) -> str:
return f"DynamicConfig({self._name}): {len(self._data)} keys"
class ConfigLoader:
"""
Modern configuration loader wit
gitextract_4l6r5una/
├── .github/
│ └── workflows/
│ └── document_deploy.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DISCLAIMER.md
├── LICENSE
├── README.md
├── README_ZH.md
├── SECURITY.md
├── aip/
│ ├── __init__.py
│ ├── endpoints/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── client_endpoint.py
│ │ ├── constellation_endpoint.py
│ │ └── server_endpoint.py
│ ├── extensions/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ └── middleware.py
│ ├── messages.py
│ ├── protocol/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── command.py
│ │ ├── device_info.py
│ │ ├── heartbeat.py
│ │ ├── registration.py
│ │ └── task_execution.py
│ ├── resilience/
│ │ ├── __init__.py
│ │ ├── heartbeat_manager.py
│ │ ├── reconnection.py
│ │ └── timeout.py
│ └── transport/
│ ├── __init__.py
│ ├── adapters.py
│ ├── base.py
│ └── websocket.py
├── config/
│ ├── __init__.py
│ ├── config_loader.py
│ ├── config_schemas.py
│ ├── galaxy/
│ │ ├── agent.yaml.template
│ │ ├── constellation.yaml
│ │ └── devices.yaml
│ └── ufo/
│ ├── agents.yaml.template
│ ├── mcp.yaml
│ ├── prices.yaml
│ ├── rag.yaml
│ ├── system.yaml
│ └── third_party.yaml
├── dataflow/
│ ├── .gitignore
│ ├── README.md
│ ├── __main__.py
│ ├── config/
│ │ ├── config.py
│ │ ├── config.yaml.template
│ │ └── config_dev.yaml
│ ├── data_flow_controller.py
│ ├── dataflow.py
│ ├── env/
│ │ └── env_manager.py
│ ├── execution/
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── execute_agent.py
│ │ │ └── execute_eval_agent.py
│ │ └── workflow/
│ │ ├── __init__.py
│ │ └── execute_flow.py
│ ├── instantiation/
│ │ ├── __init__.py
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── filter_agent.py
│ │ │ ├── prefill_agent.py
│ │ │ └── template_agent.py
│ │ └── workflow/
│ │ ├── __init__.py
│ │ ├── choose_template_flow.py
│ │ ├── filter_flow.py
│ │ └── prefill_flow.py
│ ├── prompter/
│ │ ├── __init__.py
│ │ ├── execution/
│ │ │ ├── __init__.py
│ │ │ └── execute_eval_prompter.py
│ │ └── instantiation/
│ │ ├── __init__.py
│ │ ├── filter_prompter.py
│ │ ├── prefill_prompter.py
│ │ └── template_prompter.py
│ ├── prompts/
│ │ └── instantiation/
│ │ └── visual/
│ │ ├── filter.yaml
│ │ ├── prefill.yaml
│ │ ├── prefill_example.yaml
│ │ └── template.yaml
│ ├── schema/
│ │ ├── execution_schema.json
│ │ └── instantiation_schema.json
│ └── templates/
│ └── word/
│ ├── 1.docx
│ ├── 2.docx
│ ├── 3.docx
│ ├── 4.docx
│ ├── 5.docx
│ ├── 6.docx
│ ├── 7.docx
│ ├── description.json
│ ├── template1.docx
│ └── template2.docx
├── documents/
│ ├── docs/
│ │ ├── about/
│ │ │ ├── CODE_OF_CONDUCT.md
│ │ │ ├── CONTRIBUTING.md
│ │ │ ├── DISCLAIMER.md
│ │ │ ├── LICENSE.md
│ │ │ └── SUPPORT.md
│ │ ├── aip/
│ │ │ ├── endpoints.md
│ │ │ ├── messages.md
│ │ │ ├── overview.md
│ │ │ ├── protocols.md
│ │ │ ├── resilience.md
│ │ │ └── transport.md
│ │ ├── choose_path.md
│ │ ├── client/
│ │ │ ├── computer.md
│ │ │ ├── computer_manager.md
│ │ │ ├── device_info.md
│ │ │ ├── mcp_integration.md
│ │ │ ├── overview.md
│ │ │ ├── quick_start.md
│ │ │ ├── ufo_client.md
│ │ │ └── websocket_client.md
│ │ ├── configuration/
│ │ │ ├── models/
│ │ │ │ ├── azure_openai.md
│ │ │ │ ├── claude.md
│ │ │ │ ├── custom_model.md
│ │ │ │ ├── deepseek.md
│ │ │ │ ├── gemini.md
│ │ │ │ ├── ollama.md
│ │ │ │ ├── openai.md
│ │ │ │ ├── operator.md
│ │ │ │ ├── overview.md
│ │ │ │ └── qwen.md
│ │ │ └── system/
│ │ │ ├── agents_config.md
│ │ │ ├── extending.md
│ │ │ ├── galaxy_agent.md
│ │ │ ├── galaxy_constellation.md
│ │ │ ├── galaxy_devices.md
│ │ │ ├── mcp_reference.md
│ │ │ ├── migration.md
│ │ │ ├── overview.md
│ │ │ ├── prices_config.md
│ │ │ ├── rag_config.md
│ │ │ ├── system_config.md
│ │ │ └── third_party_config.md
│ │ ├── faq.md
│ │ ├── galaxy/
│ │ │ ├── agent_registration/
│ │ │ │ ├── agent_profile.md
│ │ │ │ ├── device_registry.md
│ │ │ │ ├── overview.md
│ │ │ │ └── registration_flow.md
│ │ │ ├── client/
│ │ │ │ ├── aip_integration.md
│ │ │ │ ├── components.md
│ │ │ │ ├── constellation_client.md
│ │ │ │ ├── device_manager.md
│ │ │ │ ├── galaxy_client.md
│ │ │ │ └── overview.md
│ │ │ ├── constellation/
│ │ │ │ ├── constellation_editor.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── task_constellation.md
│ │ │ │ ├── task_star.md
│ │ │ │ └── task_star_line.md
│ │ │ ├── constellation_agent/
│ │ │ │ ├── command.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── state.md
│ │ │ │ └── strategy.md
│ │ │ ├── constellation_orchestrator/
│ │ │ │ ├── api_reference.md
│ │ │ │ ├── asynchronous_scheduling.md
│ │ │ │ ├── batched_editing.md
│ │ │ │ ├── consistency_guarantees.md
│ │ │ │ ├── constellation_manager.md
│ │ │ │ ├── event_driven_coordination.md
│ │ │ │ ├── overview.md
│ │ │ │ └── safe_assignment_locking.md
│ │ │ ├── evaluation/
│ │ │ │ ├── performance_metrics.md
│ │ │ │ ├── result_json.md
│ │ │ │ └── trajectory_report.md
│ │ │ ├── observer/
│ │ │ │ ├── agent_output_observer.md
│ │ │ │ ├── event_system.md
│ │ │ │ ├── metrics_observer.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── progress_observer.md
│ │ │ │ ├── synchronizer.md
│ │ │ │ └── visualization_observer.md
│ │ │ ├── overview.md
│ │ │ └── webui.md
│ │ ├── getting_started/
│ │ │ ├── migration_ufo2_to_galaxy.md
│ │ │ ├── more_guidance.md
│ │ │ ├── quick_start_galaxy.md
│ │ │ ├── quick_start_linux.md
│ │ │ ├── quick_start_mobile.md
│ │ │ └── quick_start_ufo2.md
│ │ ├── index.md
│ │ ├── infrastructure/
│ │ │ ├── agents/
│ │ │ │ ├── agent_types.md
│ │ │ │ ├── design/
│ │ │ │ │ ├── blackboard.md
│ │ │ │ │ ├── command.md
│ │ │ │ │ ├── memory.md
│ │ │ │ │ ├── processor.md
│ │ │ │ │ ├── prompter.md
│ │ │ │ │ ├── state.md
│ │ │ │ │ └── strategy.md
│ │ │ │ ├── overview.md
│ │ │ │ └── server_client_architecture.md
│ │ │ └── modules/
│ │ │ ├── context.md
│ │ │ ├── dispatcher.md
│ │ │ ├── overview.md
│ │ │ ├── platform_sessions.md
│ │ │ ├── round.md
│ │ │ ├── session.md
│ │ │ └── session_pool.md
│ │ ├── javascripts/
│ │ │ └── mermaid-init.js
│ │ ├── linux/
│ │ │ ├── as_galaxy_device.md
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── mcp/
│ │ │ ├── action.md
│ │ │ ├── configuration.md
│ │ │ ├── data_collection.md
│ │ │ ├── local_servers.md
│ │ │ ├── overview.md
│ │ │ ├── remote_servers.md
│ │ │ └── servers/
│ │ │ ├── app_ui_executor.md
│ │ │ ├── bash_executor.md
│ │ │ ├── command_line_executor.md
│ │ │ ├── constellation_editor.md
│ │ │ ├── excel_com_executor.md
│ │ │ ├── hardware_executor.md
│ │ │ ├── host_ui_executor.md
│ │ │ ├── mobile_executor.md
│ │ │ ├── pdf_reader_executor.md
│ │ │ ├── ppt_com_executor.md
│ │ │ ├── ui_collector.md
│ │ │ └── word_com_executor.md
│ │ ├── mobile/
│ │ │ ├── as_galaxy_device.md
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── project_directory_structure.md
│ │ ├── server/
│ │ │ ├── api.md
│ │ │ ├── client_connection_manager.md
│ │ │ ├── monitoring.md
│ │ │ ├── overview.md
│ │ │ ├── quick_start.md
│ │ │ ├── session_manager.md
│ │ │ └── websocket_handler.md
│ │ ├── tutorials/
│ │ │ ├── creating_app_agent/
│ │ │ │ ├── demonstration_provision.md
│ │ │ │ ├── help_document_provision.md
│ │ │ │ ├── overview.md
│ │ │ │ └── warpping_app_native_api.md
│ │ │ ├── creating_device_agent/
│ │ │ │ ├── client_setup.md
│ │ │ │ ├── configuration.md
│ │ │ │ ├── core_components.md
│ │ │ │ ├── example_mobile_agent.md
│ │ │ │ ├── index.md
│ │ │ │ ├── mcp_server.md
│ │ │ │ ├── overview.md
│ │ │ │ └── testing.md
│ │ │ ├── creating_mcp_servers.md
│ │ │ └── creating_third_party_agents.md
│ │ └── ufo2/
│ │ ├── advanced_usage/
│ │ │ ├── batch_mode.md
│ │ │ ├── customization.md
│ │ │ ├── follower_mode.md
│ │ │ └── operator_as_app_agent.md
│ │ ├── app_agent/
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── as_galaxy_device.md
│ │ ├── core_features/
│ │ │ ├── control_detection/
│ │ │ │ ├── hybrid_detection.md
│ │ │ │ ├── overview.md
│ │ │ │ ├── uia_detection.md
│ │ │ │ └── visual_detection.md
│ │ │ ├── hybrid_actions.md
│ │ │ ├── knowledge_substrate/
│ │ │ │ ├── experience_learning.md
│ │ │ │ ├── learning_from_bing_search.md
│ │ │ │ ├── learning_from_demonstration.md
│ │ │ │ ├── learning_from_help_document.md
│ │ │ │ └── overview.md
│ │ │ └── multi_action.md
│ │ ├── dataflow/
│ │ │ ├── execution.md
│ │ │ ├── instantiation.md
│ │ │ ├── overview.md
│ │ │ ├── result.md
│ │ │ └── windows_app_env.md
│ │ ├── evaluation/
│ │ │ ├── benchmark/
│ │ │ │ ├── osworld.md
│ │ │ │ ├── overview.md
│ │ │ │ └── windows_agent_arena.md
│ │ │ ├── evaluation_agent.md
│ │ │ └── logs/
│ │ │ ├── evaluation_logs.md
│ │ │ ├── markdown_log_viewer.md
│ │ │ ├── overview.md
│ │ │ ├── request_logs.md
│ │ │ ├── screenshots_logs.md
│ │ │ ├── step_logs.md
│ │ │ └── ui_tree_logs.md
│ │ ├── host_agent/
│ │ │ ├── commands.md
│ │ │ ├── overview.md
│ │ │ ├── state.md
│ │ │ └── strategy.md
│ │ ├── overview.md
│ │ └── prompts/
│ │ ├── basic_template.md
│ │ ├── examples_prompts.md
│ │ └── overview.md
│ └── mkdocs.yml
├── galaxy/
│ ├── README.md
│ ├── README_ZH.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── constellation_agent.py
│ │ ├── constellation_agent_states.py
│ │ ├── processors/
│ │ │ ├── processor.py
│ │ │ ├── processor_context.py
│ │ │ └── strategies/
│ │ │ ├── __init__.py
│ │ │ ├── base_constellation_strategy.py
│ │ │ ├── constellation_creation_strategy.py
│ │ │ ├── constellation_editing_strategy.py
│ │ │ └── constellation_factory.py
│ │ ├── prompters/
│ │ │ ├── __init__.py
│ │ │ ├── base_constellation_prompter.py
│ │ │ ├── constellation_creation_prompter.py
│ │ │ └── constellation_editing_prompter.py
│ │ └── schema.py
│ ├── client/
│ │ ├── __init__.py
│ │ ├── components/
│ │ │ ├── __init__.py
│ │ │ ├── connection_manager.py
│ │ │ ├── device_registry.py
│ │ │ ├── heartbeat_manager.py
│ │ │ ├── message_processor.py
│ │ │ ├── task_queue_manager.py
│ │ │ └── types.py
│ │ ├── config_loader.py
│ │ ├── constellation_client.py
│ │ ├── demo_device_events.py
│ │ ├── device_manager.py
│ │ └── support/
│ │ ├── __init__.py
│ │ ├── client_config_manager.py
│ │ └── status_manager.py
│ ├── constellation/
│ │ ├── __init__.py
│ │ ├── editor/
│ │ │ ├── __init__.py
│ │ │ ├── command_history.py
│ │ │ ├── command_interface.py
│ │ │ ├── command_invoker.py
│ │ │ ├── command_registry.py
│ │ │ ├── commands.py
│ │ │ └── constellation_editor.py
│ │ ├── enums.py
│ │ ├── orchestrator/
│ │ │ ├── constellation_manager.py
│ │ │ └── orchestrator.py
│ │ ├── task_constellation.py
│ │ ├── task_star.py
│ │ └── task_star_line.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── di_container.py
│ │ ├── events.py
│ │ ├── interfaces.py
│ │ └── types.py
│ ├── galaxy.py
│ ├── galaxy_client.py
│ ├── prompts/
│ │ └── constellation/
│ │ ├── examples/
│ │ │ ├── constellation_creation_example.yaml
│ │ │ └── constellation_editing_example.yaml
│ │ └── share/
│ │ ├── constellation_creation.yaml
│ │ └── constellation_editing.yaml
│ ├── session/
│ │ ├── __init__.py
│ │ ├── galaxy_session.py
│ │ └── observers/
│ │ ├── __init__.py
│ │ ├── agent_output_observer.py
│ │ ├── base_observer.py
│ │ ├── constellation_sync_observer.py
│ │ ├── constellation_visualization_handler.py
│ │ ├── dag_visualization_observer.py
│ │ └── task_visualization_handler.py
│ ├── trajectory/
│ │ ├── __init__.py
│ │ ├── galaxy_parser.py
│ │ └── generate_report.py
│ ├── visualization/
│ │ ├── __init__.py
│ │ ├── change_detector.py
│ │ ├── client_display.py
│ │ ├── constellation_display.py
│ │ ├── constellation_formatter.py
│ │ ├── dag_visualizer.py
│ │ └── task_display.py
│ └── webui/
│ ├── README.md
│ ├── __init__.py
│ ├── dependencies.py
│ ├── frontend/
│ │ ├── .vite/
│ │ │ └── deps_temp_3b00ab27/
│ │ │ └── package.json
│ │ ├── README.md
│ │ ├── dist/
│ │ │ ├── assets/
│ │ │ │ ├── index-Bthiy-Xd.js
│ │ │ │ └── index-DixfhFjw.css
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── components/
│ │ │ │ ├── AgentOutput.tsx
│ │ │ │ ├── ControlPanel.tsx
│ │ │ │ ├── DAGVisualization.tsx
│ │ │ │ ├── EventLog.tsx
│ │ │ │ ├── SessionView.tsx
│ │ │ │ ├── Welcome.tsx
│ │ │ │ ├── chat/
│ │ │ │ │ ├── ChatWindow.tsx
│ │ │ │ │ ├── Composer.tsx
│ │ │ │ │ └── MessageBubble.tsx
│ │ │ │ ├── common/
│ │ │ │ │ └── SearchFilterBar.tsx
│ │ │ │ ├── constellation/
│ │ │ │ │ ├── ConstellationBlock.tsx
│ │ │ │ │ ├── ConstellationStats.tsx
│ │ │ │ │ └── DagPreview.tsx
│ │ │ │ ├── devices/
│ │ │ │ │ ├── AddDeviceModal.tsx
│ │ │ │ │ └── DevicePanel.tsx
│ │ │ │ ├── layout/
│ │ │ │ │ ├── LeftSidebar.tsx
│ │ │ │ │ ├── NotificationCenter.tsx
│ │ │ │ │ ├── RightPanel.tsx
│ │ │ │ │ └── StarfieldOverlay.tsx
│ │ │ │ ├── session/
│ │ │ │ │ └── SessionControlBar.tsx
│ │ │ │ └── tasks/
│ │ │ │ ├── TaskDetailPanel.tsx
│ │ │ │ └── TaskList.tsx
│ │ │ ├── config/
│ │ │ │ └── api.ts
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── services/
│ │ │ │ └── websocket.ts
│ │ │ ├── store/
│ │ │ │ ├── galaxyStore.ts
│ │ │ │ └── mockData.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── handlers/
│ │ ├── __init__.py
│ │ └── websocket_handlers.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── enums.py
│ │ ├── requests.py
│ │ └── responses.py
│ ├── py.typed
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── devices.py
│ │ ├── health.py
│ │ └── websocket.py
│ ├── server.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── config_service.py
│ │ ├── device_service.py
│ │ └── galaxy_service.py
│ ├── templates/
│ │ └── index.html
│ └── websocket_observer.py
├── learner/
│ ├── README.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── basic.py
│ ├── doc_example/
│ │ ├── ppt-copilot.xml
│ │ └── ppt-copilot.xml.meta
│ ├── indexer.py
│ ├── json_loader.py
│ ├── learner.py
│ ├── utils.py
│ └── xml_loader.py
├── model_worker/
│ ├── README.md
│ └── custom_worker.py
├── record_processor/
│ ├── README.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── parser/
│ │ ├── demonstration_record.py
│ │ └── psr_record_parser.py
│ ├── record_processor.py
│ ├── summarizer/
│ │ └── summarizer.py
│ └── utils/
│ └── __init__.py
├── requirements.txt
├── tests/
│ ├── BUG_REPORT_REAL_SESSION_TEST.md
│ ├── README.md
│ ├── README_log_collection_test.md
│ ├── __init__.py
│ ├── aip/
│ │ ├── __init__.py
│ │ ├── test_binary_transfer.py
│ │ ├── test_endpoints.py
│ │ ├── test_integration.py
│ │ ├── test_messages.py
│ │ ├── test_protocol.py
│ │ ├── test_resilience.py
│ │ └── test_transport.py
│ ├── bug_summary_report.py
│ ├── clients/
│ │ ├── test_comprehensive_client_types.py
│ │ ├── test_constellation_client.py
│ │ ├── test_constellation_validation.py
│ │ ├── test_device_validation.py
│ │ ├── test_handler_refactoring.py
│ │ ├── test_server_client_recognition.py
│ │ └── test_ws_client_types.py
│ ├── config/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── test_attribute_access_validation.py
│ │ ├── test_config_loader.py
│ │ ├── test_galaxy_config.py
│ │ ├── test_migration.py
│ │ ├── test_migration_validation.py
│ │ └── test_validation.py
│ ├── confirm_old_handlers_restored.py
│ ├── debug_observer_output.py
│ ├── demo_device_info.py
│ ├── demo_galaxy_client_log_collection.py
│ ├── editors/
│ │ ├── CONSTELLATION_EDITOR_UPDATES.md
│ │ ├── comprehensive_demo.py
│ │ ├── constellation_editor_example.py
│ │ ├── debug_undo.py
│ │ ├── direct_json_test.py
│ │ ├── minimal_json_test.py
│ │ ├── simple_json_test.py
│ │ ├── test_constellation_editor.py
│ │ ├── test_constellation_json.py
│ │ ├── test_constellation_mcp.py
│ │ ├── test_constellation_mcp_simplified.py
│ │ ├── test_json_serialization.py
│ │ ├── test_mcp_basic.py
│ │ ├── test_only_undo.py
│ │ └── test_updated_editor.py
│ ├── examples/
│ │ ├── __init__.py
│ │ ├── auto_id_constellation.json
│ │ ├── auto_id_example.py
│ │ ├── basemodel_example.py
│ │ ├── dict_format_example.json
│ │ ├── example_constellation.json
│ │ ├── list_dict_compatibility_example.py
│ │ └── list_format_example.json
│ ├── galaxy/
│ │ ├── __init__.py
│ │ ├── client/
│ │ │ ├── README_disconnection_tests.md
│ │ │ ├── run_disconnection_tests.py
│ │ │ ├── test_device_disconnection_reconnection.py
│ │ │ ├── test_device_disconnection_task_handling.py
│ │ │ ├── test_device_events.py
│ │ │ ├── test_device_manager_assign_task.py
│ │ │ ├── test_device_manager_info_update.py
│ │ │ ├── test_galaxy_client.py
│ │ │ ├── test_galaxy_client_cancellation.py
│ │ │ ├── test_mock_and_visualization.py
│ │ │ ├── test_mock_functionality.py
│ │ │ ├── test_pending_task_cancellation.py
│ │ │ ├── test_server_restart_reconnection.py
│ │ │ ├── test_simple_mock.py
│ │ │ ├── test_target_device_not_registered.py
│ │ │ └── test_task_response_mechanism.py
│ │ ├── constellation/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── run_all_tests.py
│ │ │ ├── test_constellation_parsing.py
│ │ │ ├── test_constellation_parsing_debug.py
│ │ │ ├── test_constellation_summary.py
│ │ │ ├── test_constellation_tasks_debug.py
│ │ │ └── test_orchestrator_cancellation.py
│ │ ├── mocks.py
│ │ ├── run_cancellation_tests.py
│ │ ├── session/
│ │ │ ├── README.md
│ │ │ ├── logs/
│ │ │ │ └── galaxy/
│ │ │ │ └── Test task analyze data and generate insights/
│ │ │ │ ├── evaluation.log
│ │ │ │ ├── request.log
│ │ │ │ └── response.log
│ │ │ ├── test_galaxy_session.py
│ │ │ ├── test_galaxy_session_final.py
│ │ │ ├── test_galaxy_session_integration.py
│ │ │ ├── test_galaxy_session_proper_mock.py
│ │ │ └── test_session_cancellation.py
│ │ ├── trajectory/
│ │ │ ├── __init__.py
│ │ │ └── test_topology_visualization.py
│ │ ├── visualization/
│ │ │ └── test_constellation_formatter.py
│ │ └── webui/
│ │ ├── test_websocket_server.py
│ │ └── test_webui_stop_integration.py
│ ├── integration/
│ │ ├── galaxy/
│ │ │ ├── test_galaxy_state_machine_integration.py
│ │ │ └── test_galaxy_state_machine_simple.py
│ │ ├── test_constellation_aip_communication.py
│ │ ├── test_constellation_aip_simple.py
│ │ ├── test_constellation_server_compatibility.py
│ │ ├── test_device_communication.py
│ │ ├── test_device_info_flow.py
│ │ ├── test_e2e_galaxy.py
│ │ ├── test_e2e_simplified.py
│ │ ├── test_galaxy_state_machine_integration.py
│ │ ├── test_mobile_mcp_server.py
│ │ ├── test_mobile_mcp_standalone.py
│ │ ├── test_presenter_integration.py
│ │ └── verify_mobile_setup.py
│ ├── logs/
│ │ └── galaxy/
│ │ └── Test task analyze data and generate insights/
│ │ ├── evaluation.log
│ │ ├── request.log
│ │ └── response.log
│ ├── run_dag_tests.py
│ ├── run_device_info_tests.py
│ ├── run_galaxy_session_tests.py
│ ├── run_galaxy_state_machine_tests.py
│ ├── run_galaxy_tests.py
│ ├── run_sync_tests.py
│ ├── test_agents_config_migration.py
│ ├── test_base_constellation_prompter.py
│ ├── test_color_fix.py
│ ├── test_constellation_continuation.py
│ ├── test_constellation_manager.py
│ ├── test_constellation_observer_logger.py
│ ├── test_constellation_parser.py
│ ├── test_constellation_parser_refactored.py
│ ├── test_constellation_serializer.py
│ ├── test_constellation_sync_integration.py
│ ├── test_constellation_sync_observer.py
│ ├── test_constellation_sync_observer_simple.py
│ ├── test_constellation_update_lock.py
│ ├── test_constellation_updater.py
│ ├── test_convert_config.py
│ ├── test_dag_visualization_observer_events.py
│ ├── test_enhanced_continuation.py
│ ├── test_galaxy_client_log_collection_session.py
│ ├── test_galaxy_framework_summary.py
│ ├── test_galaxy_session_proper_mock.py
│ ├── test_linux_log_collection_excel_generation.py
│ ├── test_logger_namespace_issue.py
│ ├── test_misc_config_migration.py
│ ├── test_old_handlers_simple.py
│ ├── test_orchestrator_refactored.py
│ ├── test_prompt_sanitizer.py
│ ├── test_race_condition_real.py
│ ├── test_real_galaxy_session_integration.py
│ ├── test_realistic_constellation_observer.py
│ ├── test_server_client_config_migration.py
│ ├── test_session_observers.py
│ ├── test_session_visualization_integration.py
│ ├── unit/
│ │ ├── galaxy/
│ │ │ ├── agents/
│ │ │ │ ├── test_constellation_factory_refactor.py
│ │ │ │ ├── test_constellation_simple.py
│ │ │ │ └── test_galaxy_agent_states.py
│ │ │ └── session/
│ │ │ ├── test_galaxy_round_refactored.py
│ │ │ ├── test_modular_observers.py
│ │ │ ├── test_observer_modular_structure.py
│ │ │ └── test_observers_refactored.py
│ │ ├── schema/
│ │ │ ├── __init__.py
│ │ │ ├── test_automatic_id_assignment.py
│ │ │ ├── test_basemodel_integration.py
│ │ │ ├── test_list_dict_compatibility.py
│ │ │ └── test_optional_fields.py
│ │ ├── test_constellation_aip_migration.py
│ │ ├── test_device_info_provider.py
│ │ ├── test_event_system.py
│ │ ├── test_galaxy_state_machine.py
│ │ ├── test_presenters.py
│ │ ├── test_refactoring.py
│ │ └── test_ws_manager_device_info.py
│ └── visualization/
│ ├── debug_constellation_modified.py
│ ├── debug_observer_output.py
│ ├── debug_visualization.py
│ ├── test_comprehensive_changes.py
│ ├── test_constellation_agent_events.py
│ ├── test_constellation_agent_integration.py
│ ├── test_constellation_comparison.py
│ ├── test_constellation_events.py
│ ├── test_dag_demo.py
│ ├── test_dag_mock.py
│ ├── test_dag_simple.py
│ ├── test_dependency_property_changes.py
│ ├── test_enhanced_visualization.py
│ ├── test_individual_events.py
│ ├── test_manual_constellation_events.py
│ └── test_refactored_modules.py
├── ufo/
│ ├── README.md
│ ├── README_UFO_V1.md
│ ├── README_ZH.md
│ ├── __init__.py
│ ├── __main__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── agent/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent.py
│ │ │ ├── basic.py
│ │ │ ├── customized_agent.py
│ │ │ ├── evaluation_agent.py
│ │ │ └── host_agent.py
│ │ ├── memory/
│ │ │ ├── __init__.py
│ │ │ ├── blackboard.py
│ │ │ └── memory.py
│ │ ├── presenters/
│ │ │ ├── __init__.py
│ │ │ ├── base_presenter.py
│ │ │ ├── presenter_factory.py
│ │ │ └── rich_presenter.py
│ │ ├── processors/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent_processor.py
│ │ │ ├── context/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_agent_processing_context.py
│ │ │ │ ├── host_agent_processing_context.py
│ │ │ │ └── processing_context.py
│ │ │ ├── core/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── processing_middleware.py
│ │ │ │ ├── processor_framework.py
│ │ │ │ └── strategy_dependency.py
│ │ │ ├── customized/
│ │ │ │ └── customized_agent_processor.py
│ │ │ ├── host_agent_processor.py
│ │ │ ├── schemas/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── actions.py
│ │ │ │ ├── log_schema.py
│ │ │ │ ├── response_schema.py
│ │ │ │ └── target.py
│ │ │ └── strategies/
│ │ │ ├── __init__.py
│ │ │ ├── app_agent_processing_strategy.py
│ │ │ ├── customized_agent_processing_strategy.py
│ │ │ ├── host_agent_processing_strategy.py
│ │ │ ├── linux_agent_strategy.py
│ │ │ ├── mobile_agent_strategy.py
│ │ │ ├── processing_strategy.py
│ │ │ └── strategy_dependency.py
│ │ └── states/
│ │ ├── __init__.py
│ │ ├── app_agent_state.py
│ │ ├── basic.py
│ │ ├── evaluaton_agent_state.py
│ │ ├── host_agent_state.py
│ │ ├── linux_agent_state.py
│ │ ├── mobile_agent_state.py
│ │ └── operator_state.py
│ ├── automator/
│ │ ├── __init__.py
│ │ ├── action_execution.py
│ │ ├── app_apis/
│ │ │ ├── __init__.py
│ │ │ ├── basic.py
│ │ │ ├── excel/
│ │ │ │ ├── __init__.py
│ │ │ │ └── excelclient.py
│ │ │ ├── factory.py
│ │ │ ├── powerpoint/
│ │ │ │ ├── __init__.py
│ │ │ │ └── powerpointclient.py
│ │ │ ├── shell/
│ │ │ │ ├── __init__.py
│ │ │ │ └── shell_client.py
│ │ │ ├── web/
│ │ │ │ ├── __init__.py
│ │ │ │ └── webclient.py
│ │ │ └── word/
│ │ │ ├── __init__.py
│ │ │ └── wordclient.py
│ │ ├── basic.py
│ │ ├── puppeteer.py
│ │ └── ui_control/
│ │ ├── __init__.py
│ │ ├── control_filter.py
│ │ ├── controller.py
│ │ ├── grounding/
│ │ │ ├── __init__.py
│ │ │ ├── basic.py
│ │ │ └── omniparser.py
│ │ ├── inspector.py
│ │ ├── screenshot.py
│ │ └── ui_tree.py
│ ├── client/
│ │ ├── client.py
│ │ ├── computer.py
│ │ ├── device_info_provider.py
│ │ ├── mcp/
│ │ │ ├── http_servers/
│ │ │ │ ├── hardware_mcp_server.py
│ │ │ │ ├── linux_mcp_server.py
│ │ │ │ └── mobile_mcp_server.py
│ │ │ ├── local_servers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cli_mcp_server.py
│ │ │ │ ├── constellation_mcp_server.py
│ │ │ │ ├── excel_wincom_mcp_server.py
│ │ │ │ ├── pdf_reader_mcp_server.py
│ │ │ │ ├── ppt_wincom_mcp_server.py
│ │ │ │ ├── ui_mcp_server.py
│ │ │ │ └── word_wincom_mcp_server.py
│ │ │ ├── mcp_registry.py
│ │ │ └── mcp_server_manager.py
│ │ ├── ufo_client.py
│ │ └── websocket.py
│ ├── config/
│ │ └── __init__.py
│ ├── experience/
│ │ ├── __init__.py
│ │ ├── experience_parser.py
│ │ └── summarizer.py
│ ├── llm/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── claude.py
│ │ ├── cogagent.py
│ │ ├── config_helper.py
│ │ ├── deepseek.py
│ │ ├── gemini.py
│ │ ├── grounding_model/
│ │ │ └── omniparser_service.py
│ │ ├── llava.py
│ │ ├── llm_call.py
│ │ ├── ollama.py
│ │ ├── openai.py
│ │ ├── placeholder.py
│ │ ├── qwen.py
│ │ └── response_schema.py
│ ├── logging/
│ │ ├── __init__.py
│ │ └── setup.py
│ ├── module/
│ │ ├── __init__.py
│ │ ├── basic.py
│ │ ├── context.py
│ │ ├── dispatcher.py
│ │ ├── interactor.py
│ │ ├── session_pool.py
│ │ └── sessions/
│ │ ├── __init__.py
│ │ ├── linux_session.py
│ │ ├── mobile_session.py
│ │ ├── plan_reader.py
│ │ ├── platform_session.py
│ │ ├── service_session.py
│ │ └── session.py
│ ├── prompter/
│ │ ├── __init__.py
│ │ ├── agent_prompter.py
│ │ ├── basic.py
│ │ ├── customized/
│ │ │ ├── linux_agent_prompter.py
│ │ │ └── mobile_agent_prompter.py
│ │ ├── demonstration_prompter.py
│ │ ├── eva_prompter.py
│ │ ├── experience_prompter.py
│ │ └── prompt_sanitizer.py
│ ├── prompts/
│ │ ├── demonstration/
│ │ │ └── demonstration_summary.yaml
│ │ ├── evaluation/
│ │ │ └── evaluate.yaml
│ │ ├── examples/
│ │ │ ├── nonvisual/
│ │ │ │ ├── app_agent_example.yaml
│ │ │ │ ├── app_agent_example_as.yaml
│ │ │ │ └── host_agent_example.yaml
│ │ │ └── visual/
│ │ │ ├── app_agent_example.yaml
│ │ │ ├── app_agent_example_as.yaml
│ │ │ └── host_agent_example.yaml
│ │ ├── experience/
│ │ │ └── experience_summary.yaml
│ │ ├── share/
│ │ │ └── base/
│ │ │ ├── api.yaml
│ │ │ ├── app_agent.yaml
│ │ │ └── host_agent.yaml
│ │ └── third_party/
│ │ ├── linux_agent.yaml
│ │ ├── linux_agent_example.yaml
│ │ ├── mobile_agent.yaml
│ │ └── mobile_agent_example.yaml
│ ├── rag/
│ │ ├── __init__.py
│ │ ├── retriever.py
│ │ └── web_search.py
│ ├── server/
│ │ ├── app.py
│ │ ├── services/
│ │ │ ├── api.py
│ │ │ ├── client_connection_manager.py
│ │ │ └── session_manager.py
│ │ └── ws/
│ │ └── handler.py
│ ├── tools/
│ │ ├── README_CONFIG.md
│ │ ├── convert_config.py
│ │ ├── migrate_config.py
│ │ ├── test_config.py
│ │ └── validate_config.py
│ ├── trajectory/
│ │ └── parser.py
│ ├── ufo.py
│ └── utils/
│ └── __init__.py
└── vectordb/
└── demonstration/
└── example.yaml
Showing preview only (630K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (7039 symbols across 420 files)
FILE: aip/endpoints/base.py
class AIPEndpoint (line 18) | class AIPEndpoint(ABC):
method __init__ (line 33) | def __init__(
method start (line 59) | async def start(self) -> None:
method stop (line 68) | async def stop(self) -> None:
method handle_message (line 77) | async def handle_message(self, msg: Any) -> None:
method is_connected (line 85) | def is_connected(self) -> bool:
method send_with_timeout (line 93) | async def send_with_timeout(
method receive_with_timeout (line 106) | async def receive_with_timeout(
method reconnect_device (line 121) | async def reconnect_device(self, device_id: str) -> bool:
method cancel_device_tasks (line 131) | async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
method on_device_disconnected (line 141) | async def on_device_disconnected(self, device_id: str) -> None:
FILE: aip/endpoints/client_endpoint.py
class DeviceClientEndpoint (line 19) | class DeviceClientEndpoint(AIPEndpoint):
method __init__ (line 27) | def __init__(
method start (line 78) | async def start(self) -> None:
method stop (line 94) | async def stop(self) -> None:
method handle_message (line 108) | async def handle_message(self, msg: Any) -> None:
method reconnect_device (line 117) | async def reconnect_device(self, device_id: str) -> bool:
method cancel_device_tasks (line 131) | async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
method on_device_disconnected (line 141) | async def on_device_disconnected(self, device_id: str) -> None:
method is_connected (line 149) | def is_connected(self) -> bool:
FILE: aip/endpoints/constellation_endpoint.py
class ConstellationEndpoint (line 19) | class ConstellationEndpoint(AIPEndpoint):
method __init__ (line 26) | def __init__(
method start (line 66) | async def start(self) -> None:
method stop (line 70) | async def stop(self) -> None:
method connect_to_device (line 76) | async def connect_to_device(
method send_task_to_device (line 89) | async def send_task_to_device(self, device_id: str, task_request: Any)...
method request_device_info (line 101) | async def request_device_info(self, device_id: str) -> Optional[Dict[s...
method disconnect_device (line 110) | async def disconnect_device(self, device_id: str) -> None:
method is_device_connected (line 118) | def is_device_connected(self, device_id: str) -> bool:
method handle_message (line 127) | async def handle_message(self, msg: Any) -> None:
method reconnect_device (line 137) | async def reconnect_device(self, device_id: str) -> bool:
method cancel_device_tasks (line 153) | async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
method on_device_disconnected (line 164) | async def on_device_disconnected(self, device_id: str) -> None:
FILE: aip/endpoints/server_endpoint.py
class DeviceServerEndpoint (line 21) | class DeviceServerEndpoint(AIPEndpoint):
method __init__ (line 29) | def __init__(
method start (line 66) | async def start(self) -> None:
method stop (line 74) | async def stop(self) -> None:
method handle_websocket (line 78) | async def handle_websocket(self, websocket: WebSocket) -> None:
method handle_message (line 88) | async def handle_message(self, msg: Any) -> None:
method reconnect_device (line 97) | async def reconnect_device(self, device_id: str) -> bool:
method cancel_device_tasks (line 107) | async def cancel_device_tasks(self, device_id: str, reason: str) -> None:
method on_device_disconnected (line 121) | async def on_device_disconnected(self, device_id: str) -> None:
FILE: aip/extensions/base.py
class AIPExtension (line 14) | class AIPExtension(ABC):
method on_message_sent (line 23) | async def on_message_sent(self, msg: Any) -> None:
method on_message_received (line 32) | async def on_message_received(self, msg: Any) -> None:
method on_connection_established (line 41) | async def on_connection_established(self, endpoint_id: str) -> None:
method on_connection_closed (line 50) | async def on_connection_closed(self, endpoint_id: str) -> None:
method on_error (line 59) | async def on_error(self, error: Exception, context: str) -> None:
FILE: aip/extensions/middleware.py
class LoggingExtension (line 17) | class LoggingExtension(AIPExtension):
method __init__ (line 22) | def __init__(self, log_level: int = logging.INFO):
method on_message_sent (line 31) | async def on_message_sent(self, msg: Any) -> None:
method on_message_received (line 36) | async def on_message_received(self, msg: Any) -> None:
method on_connection_established (line 41) | async def on_connection_established(self, endpoint_id: str) -> None:
method on_connection_closed (line 45) | async def on_connection_closed(self, endpoint_id: str) -> None:
method on_error (line 49) | async def on_error(self, error: Exception, context: str) -> None:
class MetricsExtension (line 54) | class MetricsExtension(AIPExtension):
method __init__ (line 59) | def __init__(self):
method on_message_sent (line 73) | async def on_message_sent(self, msg: Any) -> None:
method on_message_received (line 86) | async def on_message_received(self, msg: Any) -> None:
method on_connection_established (line 97) | async def on_connection_established(self, endpoint_id: str) -> None:
method on_connection_closed (line 101) | async def on_connection_closed(self, endpoint_id: str) -> None:
method on_error (line 105) | async def on_error(self, error: Exception, context: str) -> None:
method get_metrics (line 109) | def get_metrics(self) -> Dict[str, Any]:
method reset_metrics (line 124) | def reset_metrics(self) -> None:
FILE: aip/messages.py
class Rect (line 34) | class Rect(BaseModel):
class ControlInfo (line 46) | class ControlInfo(BaseModel):
class WindowInfo (line 65) | class WindowInfo(ControlInfo):
class AppWindowControlInfo (line 78) | class AppWindowControlInfo(BaseModel):
class MCPToolInfo (line 92) | class MCPToolInfo(BaseModel):
class MCPToolCall (line 109) | class MCPToolCall(BaseModel):
method tool_info (line 130) | def tool_info(self) -> MCPToolInfo:
class Command (line 149) | class Command(BaseModel):
class ResultStatus (line 172) | class ResultStatus(str, Enum):
class Result (line 183) | class Result(BaseModel):
class TaskStatus (line 200) | class TaskStatus(str, Enum):
class ClientMessageType (line 224) | class ClientMessageType(str, Enum):
class ServerMessageType (line 255) | class ServerMessageType(str, Enum):
class ClientType (line 282) | class ClientType(str, Enum):
class ServerMessage (line 299) | class ServerMessage(BaseModel):
class ClientMessage (line 343) | class ClientMessage(BaseModel):
class MessageValidator (line 398) | class MessageValidator:
method validate_registration (line 407) | def validate_registration(msg: ClientMessage) -> bool:
method validate_task_request (line 424) | def validate_task_request(msg: ClientMessage) -> bool:
method validate_command_results (line 440) | def validate_command_results(msg: ClientMessage) -> bool:
method validate_server_message (line 456) | def validate_server_message(msg: ServerMessage) -> bool:
class BinaryMetadata (line 484) | class BinaryMetadata(BaseModel):
class FileTransferStart (line 506) | class FileTransferStart(BaseModel):
class FileTransferComplete (line 526) | class FileTransferComplete(BaseModel):
class ChunkMetadata (line 543) | class ChunkMetadata(BaseModel):
FILE: aip/protocol/base.py
class AIPProtocol (line 22) | class AIPProtocol:
method __init__ (line 41) | def __init__(self, transport: Transport):
method send_message (line 52) | async def send_message(self, msg: Any) -> None:
method receive_message (line 95) | async def receive_message(self, message_type: type = ServerMessage) ->...
method add_middleware (line 139) | def add_middleware(self, middleware: "ProtocolMiddleware") -> None:
method register_handler (line 151) | def register_handler(self, message_type: str, handler: MessageHandler)...
method dispatch_message (line 163) | async def dispatch_message(self, msg: Any) -> None:
method is_connected (line 181) | def is_connected(self) -> bool:
method send_error (line 185) | async def send_error(
method send_ack (line 208) | async def send_ack(
method close (line 231) | async def close(self) -> None:
method send_binary_message (line 239) | async def send_binary_message(
method receive_binary_message (line 306) | async def receive_binary_message(
method send_file (line 368) | async def send_file(
method receive_file (line 459) | async def receive_file(
class ProtocolMiddleware (line 546) | class ProtocolMiddleware(ABC):
method process_outgoing (line 555) | async def process_outgoing(self, msg: Any) -> Any:
method process_incoming (line 565) | async def process_incoming(self, msg: Any) -> Any:
class LoggingMiddleware (line 575) | class LoggingMiddleware(ProtocolMiddleware):
method __init__ (line 582) | def __init__(self, log_level: int = logging.DEBUG):
method process_outgoing (line 591) | async def process_outgoing(self, msg: Any) -> Any:
method process_incoming (line 596) | async def process_incoming(self, msg: Any) -> Any:
FILE: aip/protocol/command.py
class CommandProtocol (line 17) | class CommandProtocol(AIPProtocol):
method __init__ (line 28) | def __init__(self, *args, **kwargs):
method validate_command (line 33) | def validate_command(self, cmd: Command) -> bool:
method validate_commands (line 48) | def validate_commands(self, commands: List[Command]) -> bool:
method validate_result (line 57) | def validate_result(self, result: Result) -> bool:
method validate_results (line 69) | def validate_results(self, results: List[Result]) -> bool:
FILE: aip/protocol/device_info.py
class DeviceInfoProtocol (line 26) | class DeviceInfoProtocol(AIPProtocol):
method __init__ (line 36) | def __init__(self, *args, **kwargs):
method request_device_info (line 41) | async def request_device_info(
method send_device_info_response (line 68) | async def send_device_info_response(
method send_device_info_push (line 93) | async def send_device_info_push(
FILE: aip/protocol/heartbeat.py
class HeartbeatProtocol (line 26) | class HeartbeatProtocol(AIPProtocol):
method __init__ (line 36) | def __init__(self, *args, **kwargs):
method send_heartbeat (line 43) | async def send_heartbeat(
method send_heartbeat_ack (line 62) | async def send_heartbeat_ack(self, response_id: Optional[str] = None) ...
method start_heartbeat (line 77) | async def start_heartbeat(self, client_id: str, interval: float = 30.0...
method stop_heartbeat (line 94) | async def stop_heartbeat(self) -> None:
method _heartbeat_loop (line 105) | async def _heartbeat_loop(self, client_id: str, interval: float) -> None:
FILE: aip/protocol/registration.py
class RegistrationProtocol (line 25) | class RegistrationProtocol(AIPProtocol):
method __init__ (line 36) | def __init__(self, *args, **kwargs):
method register_as_device (line 41) | async def register_as_device(
method register_as_constellation (line 99) | async def register_as_constellation(
method send_registration_confirmation (line 171) | async def send_registration_confirmation(
method send_registration_error (line 187) | async def send_registration_error(
method _generate_response_id (line 206) | def _generate_response_id() -> str:
FILE: aip/protocol/task_execution.py
class TaskExecutionProtocol (line 28) | class TaskExecutionProtocol(AIPProtocol):
method __init__ (line 39) | def __init__(self, *args, **kwargs):
method send_task_request (line 44) | async def send_task_request(
method send_task_assignment (line 81) | async def send_task_assignment(
method send_command (line 114) | async def send_command(self, server_message: ServerMessage) -> None:
method send_commands (line 127) | async def send_commands(
method send_command_results (line 166) | async def send_command_results(
method send_task_result (line 198) | async def send_task_result(
method send_task_end (line 228) | async def send_task_end(
method send_task_end_ack (line 257) | async def send_task_end_ack(
FILE: aip/resilience/heartbeat_manager.py
class HeartbeatManager (line 18) | class HeartbeatManager:
method __init__ (line 29) | def __init__(
method start_heartbeat (line 48) | async def start_heartbeat(
method stop_heartbeat (line 72) | async def stop_heartbeat(self, client_id: str) -> None:
method stop_all (line 88) | async def stop_all(self) -> None:
method is_running (line 95) | def is_running(self, client_id: str) -> bool:
method get_interval (line 105) | def get_interval(self, client_id: str) -> Optional[float]:
method _heartbeat_loop (line 114) | async def _heartbeat_loop(self, client_id: str, interval: float) -> None:
FILE: aip/resilience/reconnection.py
class ReconnectionPolicy (line 20) | class ReconnectionPolicy(str, Enum):
class ReconnectionStrategy (line 29) | class ReconnectionStrategy:
method __init__ (line 40) | def __init__(
method handle_disconnection (line 67) | async def handle_disconnection(
method attempt_reconnection (line 108) | async def attempt_reconnection(
method _cancel_pending_tasks (line 158) | async def _cancel_pending_tasks(
method _notify_disconnection (line 178) | async def _notify_disconnection(
method _calculate_backoff (line 196) | def _calculate_backoff(self) -> float:
method reset (line 216) | def reset(self) -> None:
FILE: aip/resilience/timeout.py
class TimeoutManager (line 17) | class TimeoutManager:
method __init__ (line 28) | def __init__(self, default_timeout: float = 120.0):
method with_timeout (line 37) | async def with_timeout(
method with_timeout_or_none (line 67) | async def with_timeout_or_none(
FILE: aip/transport/adapters.py
class WebSocketAdapter (line 21) | class WebSocketAdapter(ABC):
method send (line 30) | async def send(self, data: str) -> None:
method receive (line 40) | async def receive(self) -> str:
method send_bytes (line 50) | async def send_bytes(self, data: bytes) -> None:
method receive_bytes (line 63) | async def receive_bytes(self) -> bytes:
method receive_auto (line 76) | async def receive_auto(self) -> Union[str, bytes]:
method close (line 89) | async def close(self) -> None:
method is_open (line 96) | def is_open(self) -> bool:
class FastAPIWebSocketAdapter (line 105) | class FastAPIWebSocketAdapter(WebSocketAdapter):
method __init__ (line 113) | def __init__(self, websocket):
method send (line 123) | async def send(self, data: str) -> None:
method receive (line 127) | async def receive(self) -> str:
method send_bytes (line 131) | async def send_bytes(self, data: bytes) -> None:
method receive_bytes (line 139) | async def receive_bytes(self) -> bytes:
method receive_auto (line 148) | async def receive_auto(self) -> Union[str, bytes]:
method close (line 163) | async def close(self) -> None:
method is_open (line 167) | def is_open(self) -> bool:
class WebSocketsLibAdapter (line 174) | class WebSocketsLibAdapter(WebSocketAdapter):
method __init__ (line 182) | def __init__(self, websocket: WebSocketClientProtocol):
method send (line 190) | async def send(self, data: str) -> None:
method receive (line 194) | async def receive(self) -> str:
method send_bytes (line 202) | async def send_bytes(self, data: bytes) -> None:
method receive_bytes (line 211) | async def receive_bytes(self) -> bytes:
method receive_auto (line 225) | async def receive_auto(self) -> Union[str, bytes]:
method close (line 234) | async def close(self) -> None:
method is_open (line 238) | def is_open(self) -> bool:
function create_adapter (line 243) | def create_adapter(websocket) -> WebSocketAdapter:
FILE: aip/transport/base.py
class TransportState (line 16) | class TransportState(str, Enum):
class Transport (line 34) | class Transport(ABC):
method __init__ (line 48) | def __init__(self):
method state (line 53) | def state(self) -> TransportState:
method is_connected (line 58) | def is_connected(self) -> bool:
method connect (line 63) | async def connect(self, url: str, **kwargs) -> None:
method send (line 74) | async def send(self, data: bytes) -> None:
method receive (line 85) | async def receive(self) -> bytes:
method close (line 98) | async def close(self) -> None:
method wait_closed (line 107) | async def wait_closed(self) -> None:
method __repr__ (line 115) | def __repr__(self) -> str:
FILE: aip/transport/websocket.py
class WebSocketTransport (line 24) | class WebSocketTransport(Transport):
method __init__ (line 52) | def __init__(
method connect (line 88) | async def connect(self, url: str, **kwargs) -> None:
method send (line 131) | async def send(self, data: bytes) -> None:
method receive (line 176) | async def receive(self) -> bytes:
method close (line 217) | async def close(self) -> None:
method wait_closed (line 238) | async def wait_closed(self) -> None:
method send_binary (line 248) | async def send_binary(self, data: bytes) -> None:
method receive_binary (line 298) | async def receive_binary(self) -> bytes:
method receive_auto (line 350) | async def receive_auto(self) -> Union[bytes, str]:
method websocket (line 410) | def websocket(self) -> Optional[WebSocketClientProtocol]:
method __repr__ (line 418) | def __repr__(self) -> str:
FILE: config/config_loader.py
class DynamicConfig (line 59) | class DynamicConfig:
method __init__ (line 76) | def __init__(self, data: Dict[str, Any], name: str = "config"):
method __getattr__ (line 92) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 113) | def __getitem__(self, key: str) -> Any:
method __contains__ (line 119) | def __contains__(self, key: str) -> bool:
method get (line 123) | def get(self, key: str, default: Any = None) -> Any:
method keys (line 129) | def keys(self) -> List[str]:
method items (line 133) | def items(self):
method values (line 137) | def values(self):
method to_dict (line 141) | def to_dict(self) -> Dict[str, Any]:
method __repr__ (line 145) | def __repr__(self) -> str:
method __str__ (line 148) | def __str__(self) -> str:
class ConfigLoader (line 152) | class ConfigLoader:
method __init__ (line 182) | def __init__(self, base_path: str = "config"):
method get_instance (line 194) | def get_instance(cls, base_path: str = "config") -> "ConfigLoader":
method reset (line 206) | def reset(cls) -> None:
method _load_yaml (line 210) | def _load_yaml(self, path: Path) -> Optional[Dict[str, Any]]:
method _deep_merge (line 236) | def _deep_merge(self, target: Dict[str, Any], source: Dict[str, Any]) ...
method _expand_env_vars (line 256) | def _expand_env_vars(self, value: Any) -> Any:
method _discover_yaml_files (line 279) | def _discover_yaml_files(self, directory: Path) -> List[Path]:
method _load_module_configs (line 302) | def _load_module_configs(
method _load_with_fallback (line 339) | def _load_with_fallback(
method _warn_duplicate_configs (line 400) | def _warn_duplicate_configs(
method _warn_legacy_config (line 428) | def _warn_legacy_config(self, module: str, legacy_path: str, new_path:...
method load_ufo_config (line 455) | def load_ufo_config(self, env: Optional[str] = None) -> UFOConfig:
method load_galaxy_config (line 488) | def load_galaxy_config(self, env: Optional[str] = None) -> GalaxyConfig:
method _apply_legacy_transforms (line 511) | def _apply_legacy_transforms(self, config: Dict[str, Any]) -> None:
method _update_api_base (line 533) | def _update_api_base(config: Dict[str, Any], agent_key: str) -> None:
function get_ufo_config (line 575) | def get_ufo_config(reload: bool = False) -> UFOConfig:
function get_galaxy_config (line 615) | def get_galaxy_config(reload: bool = False) -> GalaxyConfig:
function clear_config_cache (line 655) | def clear_config_cache():
FILE: config/config_schemas.py
class AgentConfig (line 15) | class AgentConfig:
method __getattr__ (line 45) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 71) | def __getitem__(self, key: str) -> Any:
method __contains__ (line 81) | def __contains__(self, key: str) -> bool:
method get (line 85) | def get(self, key: str, default: Any = None) -> Any:
method to_dict (line 92) | def to_dict(self) -> Dict[str, Any]:
method from_dict (line 118) | def from_dict(cls, data: Dict[str, Any]) -> "AgentConfig":
class RAGConfig (line 161) | class RAGConfig:
method __getattr__ (line 178) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 209) | def __getitem__(self, key: str) -> Any:
method get (line 216) | def get(self, key: str, default: Any = None) -> Any:
method from_dict (line 223) | def from_dict(cls, data: Dict[str, Any]) -> "RAGConfig":
class SystemConfig (line 258) | class SystemConfig:
method __getattr__ (line 433) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 458) | def __getitem__(self, key: str) -> Any:
method get (line 465) | def get(self, key: str, default: Any = None) -> Any:
method from_dict (line 472) | def from_dict(cls, data: Dict[str, Any]) -> "SystemConfig":
class UFOConfig (line 600) | class UFOConfig:
method __getattr__ (line 622) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 647) | def __getitem__(self, key: str) -> Any:
method __contains__ (line 655) | def __contains__(self, key: str) -> bool:
method get (line 659) | def get(self, key: str, default: Any = None) -> Any:
method keys (line 663) | def keys(self):
method items (line 667) | def items(self):
method values (line 671) | def values(self):
method to_dict (line 675) | def to_dict(self) -> Dict[str, Any]:
method from_dict (line 683) | def from_dict(cls, data: Dict[str, Any]) -> "UFOConfig":
class ConstellationRuntimeConfig (line 698) | class ConstellationRuntimeConfig:
method __getattr__ (line 715) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 740) | def __getitem__(self, key: str) -> Any:
method get (line 747) | def get(self, key: str, default: Any = None) -> Any:
method from_dict (line 754) | def from_dict(cls, data: Dict[str, Any]) -> "ConstellationRuntimeConfig":
class GalaxyAgentConfig (line 780) | class GalaxyAgentConfig:
method __getattr__ (line 787) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 795) | def __getitem__(self, key: str) -> Any:
method from_dict (line 801) | def from_dict(cls, data: Dict[str, Any]) -> "GalaxyAgentConfig":
class GalaxyConfig (line 811) | class GalaxyConfig:
method __getattr__ (line 828) | def __getattr__(self, name: str) -> Any:
method __getitem__ (line 847) | def __getitem__(self, key: str) -> Any:
method __contains__ (line 851) | def __contains__(self, key: str) -> bool:
method get (line 855) | def get(self, key: str, default: Any = None) -> Any:
method keys (line 859) | def keys(self):
method items (line 862) | def items(self):
method from_dict (line 866) | def from_dict(cls, data: Dict[str, Any]) -> "GalaxyConfig":
FILE: dataflow/config/config.py
class Config (line 7) | class Config(Config):
method __init__ (line 10) | def __init__(self, config_path="dataflow/config/"):
method get_instance (line 19) | def get_instance():
method optimize_configs (line 30) | def optimize_configs(self, configs):
FILE: dataflow/data_flow_controller.py
class AppEnum (line 37) | class AppEnum(Enum):
method __init__ (line 46) | def __init__(self, id: int, description: str, file_extension: str, win...
class TaskObject (line 62) | class TaskObject:
method __init__ (line 63) | def __init__(self, task_file_path: str, task_type: str) -> None:
method _choose_app_from_json (line 79) | def _choose_app_from_json(self, task_app: str) -> AppEnum:
method _init_attr (line 91) | def _init_attr(self, task_type: str, task_json_file: Dict[str, Any]) -...
class DataFlowController (line 111) | class DataFlowController:
method __init__ (line 116) | def __init__(self, task_path: str, task_type: str) -> None:
method init_task_info (line 134) | def init_task_info(self) -> Dict[str, Any]:
method _load_schema (line 161) | def _load_schema(self, task_type: str) -> Dict[str, Any]:
method execute_instantiation (line 173) | def execute_instantiation(self) -> Optional[List[Dict[str, Any]]]:
method execute_execution (line 214) | def execute_execution(self, request: str, plan: Dict[str, any]) -> None:
method instantiation_single_flow (line 261) | def instantiation_single_flow(
method save_result (line 296) | def save_result(self) -> None:
method template_copied_path (line 354) | def template_copied_path(self) -> str:
method instantiated_plan (line 363) | def instantiated_plan(self) -> List[Dict[str, Any]]:
method instantiated_plan (line 374) | def instantiated_plan(self, value: List[Dict[str, Any]]) -> None:
method reformat_to_batch (line 387) | def reformat_to_batch(self, path) -> None:
method run (line 431) | def run(self) -> None:
FILE: dataflow/dataflow.py
function parse_args (line 10) | def parse_args() -> argparse.Namespace:
function validate_path (line 49) | def validate_path(path: str) -> str:
function process_task (line 64) | def process_task(task_path: str, task_type: str) -> None:
function process_batch (line 81) | def process_batch(task_dir: str, task_type: str) -> None:
function main (line 99) | def main():
FILE: dataflow/env/env_manager.py
class WindowsAppEnv (line 32) | class WindowsAppEnv:
method __init__ (line 37) | def __init__(self, app_object: object) -> None:
method start (line 48) | def start(self, copied_template_path: str) -> None:
method close (line 65) | def close(self) -> None:
method _check_and_kill_process (line 85) | def _check_and_kill_process(self) -> None:
method find_matching_window (line 99) | def find_matching_window(self, doc_name: str) -> Optional[UIAWrapper]:
method _match_window_name (line 115) | def _match_window_name(self, window_title: str, doc_name: str) -> bool:
method _calculate_match_score (line 145) | def _calculate_match_score(self, control: ControlInfo, control_text: s...
method find_matching_controller (line 165) | def find_matching_controller(
FILE: dataflow/execution/agent/execute_agent.py
class ExecuteAgent (line 7) | class ExecuteAgent(AppAgent):
method __init__ (line 12) | def __init__(
FILE: dataflow/execution/agent/execute_eval_agent.py
class ExecuteEvalAgent (line 9) | class ExecuteEvalAgent(EvaluationAgent):
method __init__ (line 14) | def __init__(
method get_prompter (line 42) | def get_prompter(
FILE: dataflow/execution/workflow/execute_flow.py
class ExecuteFlow (line 19) | class ExecuteFlow(AppAgentProcessor):
method __init__ (line 27) | def __init__(
method _get_or_create_execute_agent (line 54) | def _get_or_create_execute_agent(self) -> ExecuteAgent:
method _get_or_create_evaluation_agent (line 68) | def _get_or_create_evaluation_agent(self) -> ExecuteEvalAgent:
method _initialize_logs (line 85) | def _initialize_logs(self, log_path: str) -> None:
method execute (line 98) | def execute(
method execute_plan (line 129) | def execute_plan(
method process (line 218) | def process(self) -> None:
method print_step_info (line 230) | def print_step_info(self) -> None:
method log_save (line 243) | def log_save(self) -> None:
method _parse_step_plan (line 262) | def _parse_step_plan(self, step_plan: Dict[str, Any]) -> None:
method init_and_final_capture_screenshot (line 282) | def init_and_final_capture_screenshot(self) -> None:
method execute_action (line 303) | def execute_action(self) -> None:
method general_error_handler (line 373) | def general_error_handler(self) -> None:
FILE: dataflow/instantiation/agent/filter_agent.py
class FilterAgent (line 10) | class FilterAgent(BasicAgent):
method __init__ (line 15) | def __init__(
method get_prompter (line 43) | def get_prompter(
method message_constructor (line 57) | def message_constructor(self, request: str, app: str) -> List[str]:
method process_confirmation (line 77) | def process_confirmation(self) -> None:
FILE: dataflow/instantiation/agent/prefill_agent.py
class PrefillAgent (line 11) | class PrefillAgent(BasicAgent):
method __init__ (line 16) | def __init__(
method get_prompter (line 44) | def get_prompter(
method message_constructor (line 59) | def message_constructor(
method process_confirmation (line 88) | def process_confirmation(self) -> None:
FILE: dataflow/instantiation/agent/template_agent.py
class TemplateAgent (line 11) | class TemplateAgent(BasicAgent):
method __init__ (line 16) | def __init__(
method get_prompter (line 39) | def get_prompter(
method message_constructor (line 56) | def message_constructor(
method process_confirmation (line 81) | def process_confirmation(self) -> None:
FILE: dataflow/instantiation/workflow/choose_template_flow.py
class ChooseTemplateFlow (line 21) | class ChooseTemplateFlow:
method __init__ (line 28) | def __init__(self, app_name: str, task_file_name: str, file_extension:...
method execute (line 44) | def execute(self) -> str:
method _create_copied_file (line 59) | def _create_copied_file(
method _generate_copied_file_path (line 82) | def _generate_copied_file_path(self, folder_path: Path, file_name: str...
method _get_chosen_file_path (line 96) | def _get_chosen_file_path(self) -> str:
method _choose_random_template (line 117) | def _choose_random_template(self) -> str:
method _choose_template_and_copy (line 133) | def _choose_template_and_copy(self) -> str:
method _choose_target_template_file (line 152) | def _choose_target_template_file(
method _choose_target_template_file_semantic (line 178) | def _choose_target_template_file_semantic(
method _choose_target_template_file_llm (line 200) | def _choose_target_template_file_llm(
method _load_embedding_model (line 228) | def _load_embedding_model(model_name: str) -> CacheBackedEmbeddings:
FILE: dataflow/instantiation/workflow/filter_flow.py
class FilterFlow (line 14) | class FilterFlow:
method __init__ (line 21) | def __init__(self, app_name: str, task_file_name: str) -> None:
method _get_or_create_filter_agent (line 34) | def _get_or_create_filter_agent(self) -> FilterAgent:
method execute (line 51) | def execute(self, instantiated_request: str) -> Dict[str, Any]:
method _initialize_logs (line 73) | def _initialize_logs(self) -> None:
method _get_filtered_result (line 86) | def _get_filtered_result(self, instantiated_request: str) -> Tuple[boo...
method _fix_json_commas (line 132) | def _fix_json_commas(self, json_string: str) -> str:
FILE: dataflow/instantiation/workflow/prefill_flow.py
class PrefillFlow (line 21) | class PrefillFlow(AppAgentProcessor):
method __init__ (line 28) | def __init__(
method execute (line 79) | def execute(
method _instantiate_task (line 105) | def _instantiate_task(
method _update_state (line 134) | def _update_state(self, file_path: str) -> None:
method _get_prefill_actions (line 174) | def _get_prefill_actions(
method _log_message (line 225) | def _log_message(self, prompt_message: str) -> None:
method _log_response (line 238) | def _log_response(
method _save_screenshot (line 255) | def _save_screenshot(self, doc_name: str, save_path: str) -> None:
FILE: dataflow/prompter/execution/execute_eval_prompter.py
class ExecuteEvalAgentPrompter (line 11) | class ExecuteEvalAgentPrompter(EvaluationAgentPrompter):
method __init__ (line 16) | def __init__(
method load_logs (line 42) | def load_logs(log_path: str) -> List[Dict[str, str]]:
FILE: dataflow/prompter/instantiation/filter_prompter.py
class FilterPrompter (line 11) | class FilterPrompter(BasicPrompter):
method __init__ (line 16) | def __init__(
method api_prompt_helper (line 36) | def api_prompt_helper(self, apis: Dict = {}, verbose: int = 1) -> str:
method system_prompt_construction (line 84) | def system_prompt_construction(self, app: str = "") -> str:
method user_prompt_construction (line 98) | def user_prompt_construction(self, request: str) -> str:
method user_content_construction (line 110) | def user_content_construction(self, request: str) -> List[Dict]:
method examples_prompt_helper (line 125) | def examples_prompt_helper(
FILE: dataflow/prompter/instantiation/prefill_prompter.py
class PrefillPrompter (line 12) | class PrefillPrompter(BasicPrompter):
method __init__ (line 17) | def __init__(
method api_prompt_helper (line 37) | def api_prompt_helper(self, verbose: int = 1) -> str:
method system_prompt_construction (line 67) | def system_prompt_construction(self, additional_examples: List = []) -...
method user_prompt_construction (line 78) | def user_prompt_construction(
method load_screenshots (line 95) | def load_screenshots(self, log_path: str) -> str:
method user_content_construction (line 108) | def user_content_construction(
method examples_prompt_helper (line 142) | def examples_prompt_helper(
FILE: dataflow/prompter/instantiation/template_prompter.py
class TemplatePrompter (line 13) | class TemplatePrompter(BasicPrompter):
method __init__ (line 18) | def __init__(
method encode_image (line 32) | def encode_image(self, image_path: str) -> str:
method file_prompt_helper (line 46) | def file_prompt_helper(self, path) -> str:
method system_prompt_construction (line 70) | def system_prompt_construction(self, descriptions: str = "") -> str:
method user_prompt_construction (line 84) | def user_prompt_construction(self, request: str) -> str:
method user_content_construction (line 96) | def user_content_construction(self, path: str, request: str) -> List[D...
FILE: documents/docs/javascripts/mermaid-init.js
function initMermaid (line 10) | function initMermaid() {
FILE: galaxy/agents/constellation_agent.py
class ConstellationAgent (line 45) | class ConstellationAgent(BasicAgent, IRequestProcessor, IResultProcessor):
method __init__ (line 65) | def __init__(
method current_constellation (line 101) | def current_constellation(self) -> Optional[TaskConstellation]:
method _initialize_prompter (line 111) | async def _initialize_prompter(self, context: Context) -> None:
method _ensure_context_provision (line 120) | async def _ensure_context_provision(self, context: Context) -> None:
method _create_and_process (line 130) | async def _create_and_process(self, context: Context) -> Tuple[float, ...
method _update_agent_status (line 145) | def _update_agent_status(self) -> None:
method _validate_and_update_constellation (line 152) | async def _validate_and_update_constellation(
method _create_timing_info (line 170) | def _create_timing_info(
method _sync_constellation_to_mcp (line 187) | async def _sync_constellation_to_mcp(
method _log_constellation_state (line 209) | def _log_constellation_state(
method _log_task_statuses (line 221) | def _log_task_statuses(
method _publish_constellation_modified_event (line 236) | async def _publish_constellation_modified_event(
method _handle_constellation_completion (line 272) | async def _handle_constellation_completion(
method process_creation (line 307) | async def process_creation(
method process_editing (line 340) | async def process_editing(
method context_provision (line 417) | async def context_provision(
method _load_mcp_context (line 428) | async def _load_mcp_context(
method get_prompter (line 480) | def get_prompter(self, weaving_mode: WeavingMode) -> BaseConstellation...
method message_constructor (line 490) | def message_constructor(
method process_confirmation (line 517) | async def process_confirmation(self, context: Context = None) -> bool:
method print_response (line 528) | def print_response(
method default_state (line 555) | def default_state(self):
method status_manager (line 566) | def status_manager(self):
method orchestrator (line 572) | def orchestrator(self) -> TaskConstellationOrchestrator:
method task_completion_queue (line 580) | def task_completion_queue(self) -> asyncio.Queue[TaskEvent]:
method constellation_completion_queue (line 588) | def constellation_completion_queue(self) -> asyncio.Queue[Constellatio...
method add_task_completion_event (line 595) | async def add_task_completion_event(self, event: TaskEvent) -> None:
method add_constellation_completion_event (line 632) | async def add_constellation_completion_event(
FILE: galaxy/agents/constellation_agent_states.py
class ConstellationAgentStatus (line 24) | class ConstellationAgentStatus(Enum):
class ConstellationAgentStateManager (line 33) | class ConstellationAgentStateManager(AgentStateManager):
method none_state (line 39) | def none_state(self) -> AgentState:
class ConstellationAgentState (line 43) | class ConstellationAgentState(AgentState):
method agent_class (line 47) | def agent_class(cls):
method next_state (line 52) | def next_state(self, agent: "ConstellationAgent") -> AgentState:
class StartConstellationAgentState (line 64) | class StartConstellationAgentState(ConstellationAgentState):
method handle (line 67) | async def handle(self, agent: "ConstellationAgent", context: Context) ...
method next_agent (line 129) | def next_agent(self, agent):
method is_round_end (line 132) | def is_round_end(self) -> bool:
method is_subtask_end (line 135) | def is_subtask_end(self) -> bool:
method name (line 139) | def name(cls) -> str:
class ContinueConstellationAgentState (line 144) | class ContinueConstellationAgentState(ConstellationAgentState):
method _get_merged_constellation (line 147) | async def _get_merged_constellation(
method handle (line 182) | async def handle(self, agent: "ConstellationAgent", context=None) -> N...
method next_agent (line 236) | def next_agent(self, agent):
method is_round_end (line 239) | def is_round_end(self) -> bool:
method is_subtask_end (line 242) | def is_subtask_end(self) -> bool:
method name (line 246) | def name(cls) -> str:
class FinishConstellationAgentState (line 251) | class FinishConstellationAgentState(ConstellationAgentState):
method handle (line 254) | async def handle(self, agent: "ConstellationAgent", context=None) -> N...
method next_state (line 258) | def next_state(self, agent: "ConstellationAgent") -> AgentState:
method next_agent (line 261) | def next_agent(self, agent: "ConstellationAgent"):
method is_round_end (line 264) | def is_round_end(self) -> bool:
method is_subtask_end (line 267) | def is_subtask_end(self) -> bool:
method name (line 271) | def name(cls) -> str:
class FailConstellationAgentState (line 276) | class FailConstellationAgentState(ConstellationAgentState):
method handle (line 279) | async def handle(self, agent: "ConstellationAgent", context=None) -> N...
method next_state (line 283) | def next_state(self, agent: "ConstellationAgent") -> AgentState:
method next_agent (line 286) | def next_agent(self, agent: "ConstellationAgent"):
method is_round_end (line 289) | def is_round_end(self) -> bool:
method is_subtask_end (line 292) | def is_subtask_end(self) -> bool:
method name (line 296) | def name(cls) -> str:
FILE: galaxy/agents/processors/processor.py
class ConstellationAgentProcessor (line 36) | class ConstellationAgentProcessor(ProcessorTemplate):
method __init__ (line 57) | def __init__(self, agent: "ConstellationAgent", global_context: Contex...
method _setup_strategies (line 67) | def _setup_strategies(self) -> None:
method _setup_middleware (line 96) | def _setup_middleware(self) -> None:
method _get_processor_specific_context_data (line 106) | def _get_processor_specific_context_data(self) -> Dict[str, Any]:
method _finalize_processing_context (line 131) | def _finalize_processing_context(
class ConstellationLoggingMiddleware (line 148) | class ConstellationLoggingMiddleware(EnhancedLoggingMiddleware):
method __init__ (line 159) | def __init__(self) -> None:
method before_process (line 163) | async def before_process(
method on_error (line 201) | async def on_error(self, processor: ProcessorTemplate, error: Exceptio...
FILE: galaxy/agents/processors/processor_context.py
class ConstellationProcessorContext (line 11) | class ConstellationProcessorContext(BasicProcessorContext):
method selected_keys (line 48) | def selected_keys(self) -> List[str]:
method to_dict (line 77) | def to_dict(self, selective: bool = True) -> Dict[str, Any]:
FILE: galaxy/agents/processors/strategies/base_constellation_strategy.py
class ConstellationLLMInteractionStrategy (line 61) | class ConstellationLLMInteractionStrategy(BaseProcessingStrategy):
method __init__ (line 74) | def __init__(self, fail_fast: bool = True) -> None:
method execute (line 84) | async def execute(
method _build_comprehensive_prompt (line 145) | async def _build_comprehensive_prompt(
method _log_request_data (line 185) | def _log_request_data(
method _get_llm_response_with_retry (line 220) | async def _get_llm_response_with_retry(
method _parse_and_validate_response (line 266) | def _parse_and_validate_response(
method _validate_response_fields (line 290) | def _validate_response_fields(self, response: ConstellationAgentRespon...
class BaseConstellationActionExecutionStrategy (line 312) | class BaseConstellationActionExecutionStrategy(BaseProcessingStrategy):
method __init__ (line 325) | def __init__(self, weaving_mode: WeavingMode, fail_fast: bool = False)...
method execute (line 337) | async def execute(
method _create_mode_specific_action_info (line 387) | async def _create_mode_specific_action_info(
method publish_actions (line 396) | async def publish_actions(
method sync_constellation (line 408) | def sync_constellation(
method _execute_constellation_action (line 418) | async def _execute_constellation_action(
method _action_to_command (line 452) | def _action_to_command(self, action: ActionCommandInfo) -> Command:
method _create_action_info (line 462) | def _create_action_info(
class ConstellationMemoryUpdateStrategy (line 506) | class ConstellationMemoryUpdateStrategy(BaseProcessingStrategy):
method __init__ (line 514) | def __init__(self, fail_fast: bool = False) -> None:
method execute (line 521) | async def execute(
method _create_additional_memory_data (line 563) | def _create_additional_memory_data(
method _create_and_populate_memory_item (line 625) | def _create_and_populate_memory_item(
method _update_structural_logs (line 652) | def _update_structural_logs(
FILE: galaxy/agents/processors/strategies/constellation_creation_strategy.py
class ConstellationCreationActionExecutionStrategy (line 33) | class ConstellationCreationActionExecutionStrategy(
method __init__ (line 44) | def __init__(self, fail_fast: bool = False) -> None:
method _create_mode_specific_action_info (line 51) | async def _create_mode_specific_action_info(
method publish_actions (line 91) | async def publish_actions(
method sync_constellation (line 156) | def sync_constellation(
FILE: galaxy/agents/processors/strategies/constellation_editing_strategy.py
class ConstellationEditingActionExecutionStrategy (line 32) | class ConstellationEditingActionExecutionStrategy(
method __init__ (line 43) | def __init__(self, fail_fast: bool = False) -> None:
method _create_mode_specific_action_info (line 50) | async def _create_mode_specific_action_info(
method publish_actions (line 78) | async def publish_actions(
method sync_constellation (line 105) | def sync_constellation(
FILE: galaxy/agents/processors/strategies/constellation_factory.py
class ConstellationStrategyFactory (line 27) | class ConstellationStrategyFactory:
method create_llm_interaction_strategy (line 47) | def create_llm_interaction_strategy(
method create_action_execution_strategy (line 62) | def create_action_execution_strategy(
method create_memory_update_strategy (line 82) | def create_memory_update_strategy(
method create_all_strategies (line 94) | def create_all_strategies(
method get_supported_weaving_modes (line 121) | def get_supported_weaving_modes(cls) -> list[WeavingMode]:
function create_constellation_strategies_for_mode (line 133) | def create_constellation_strategies_for_mode(
FILE: galaxy/agents/prompters/base_constellation_prompter.py
class BaseConstellationPrompter (line 27) | class BaseConstellationPrompter(BasicPrompter, ABC):
method __init__ (line 35) | def __init__(self, prompt_template: str, example_prompt_template: str):
method _format_agent_profile (line 45) | def _format_agent_profile(self, device_info: Dict[str, AgentProfile]) ...
method _format_constellation (line 87) | def _format_constellation(self, constellation: TaskConstellation) -> str:
method user_content_construction (line 237) | def user_content_construction(
method system_prompt_construction (line 255) | def system_prompt_construction(self) -> str:
method user_prompt_construction (line 268) | def user_prompt_construction(
method examples_prompt_helper (line 290) | def examples_prompt_helper(
method create_api_prompt_template (line 330) | def create_api_prompt_template(self, tools: List[MCPToolInfo]):
class ConstellationPrompterFactory (line 340) | class ConstellationPrompterFactory:
method create_prompter (line 358) | def create_prompter(
method get_supported_weaving_modes (line 401) | def get_supported_weaving_modes(cls) -> list[WeavingMode]:
FILE: galaxy/agents/prompters/constellation_creation_prompter.py
class ConstellationCreationPrompter (line 13) | class ConstellationCreationPrompter(BaseConstellationPrompter):
FILE: galaxy/agents/prompters/constellation_editing_prompter.py
class ConstellationEditingPrompter (line 13) | class ConstellationEditingPrompter(BaseConstellationPrompter):
FILE: galaxy/agents/schema.py
class IDManager (line 13) | class IDManager:
method __new__ (line 22) | def __new__(cls):
method generate_constellation_id (line 35) | def generate_constellation_id(self) -> str:
method generate_task_id (line 39) | def generate_task_id(
method generate_line_id (line 64) | def generate_line_id(
method register_existing_id (line 89) | def register_existing_id(self, constellation_id: str, id_type: str, id...
method is_task_id_available (line 104) | def is_task_id_available(self, constellation_id: str, task_id: str) ->...
method is_line_id_available (line 110) | def is_line_id_available(self, constellation_id: str, line_id: str) ->...
class WeavingMode (line 117) | class WeavingMode(str, Enum):
class TaskStarSchema (line 126) | class TaskStarSchema(BaseModel):
method convert_priority (line 156) | def convert_priority(cls, v):
method convert_status (line 166) | def convert_status(cls, v):
method convert_device_type (line 174) | def convert_device_type(cls, v):
method generate_task_id (line 184) | def generate_task_id(cls, data):
class TaskStarLineSchema (line 193) | class TaskStarLineSchema(BaseModel):
method convert_dependency_type (line 212) | def convert_dependency_type(cls, v):
method generate_line_id (line 220) | def generate_line_id(cls, data):
class TaskConstellationSchema (line 229) | class TaskConstellationSchema(BaseModel):
method convert_state (line 252) | def convert_state(cls, v):
method generate_constellation_id (line 260) | def generate_constellation_id(cls, data):
method convert_lists_to_dicts (line 273) | def convert_lists_to_dicts(cls, data):
method validate_unique_ids (line 315) | def validate_unique_ids(self):
method get_tasks_as_list (line 341) | def get_tasks_as_list(self) -> List[TaskStarSchema]:
method get_dependencies_as_list (line 345) | def get_dependencies_as_list(self) -> List[TaskStarLineSchema]:
method to_dict_with_lists (line 349) | def to_dict_with_lists(self) -> Dict[str, Any]:
class ConstellationAgentResponse (line 361) | class ConstellationAgentResponse(BaseModel):
class ConstellationRequestLog (line 374) | class ConstellationRequestLog:
FILE: galaxy/client/components/connection_manager.py
class WebSocketConnectionManager (line 37) | class WebSocketConnectionManager:
method __init__ (line 43) | def __init__(self, task_name: str):
method connect_to_device (line 67) | async def connect_to_device(
method _cleanup_device_protocols (line 154) | def _cleanup_device_protocols(self, device_id: str) -> None:
method _register_constellation_client (line 168) | async def _register_constellation_client(self, device_info: AgentProfi...
method send_task_to_device (line 266) | async def send_task_to_device(
method _wait_for_task_response (line 354) | async def _wait_for_task_response(
method complete_task_response (line 399) | def complete_task_response(self, task_id: str, response: ServerMessage...
method is_connected (line 446) | def is_connected(self, device_id: str) -> bool:
method disconnect_device (line 451) | async def disconnect_device(self, device_id: str) -> None:
method _cancel_pending_tasks_for_device (line 474) | def _cancel_pending_tasks_for_device(self, device_id: str) -> None:
method disconnect_all (line 510) | async def disconnect_all(self) -> None:
method request_device_info (line 515) | async def request_device_info(self, device_id: str) -> Optional[Dict[s...
method complete_device_info_response (line 584) | def complete_device_info_response(
method complete_registration_response (line 614) | def complete_registration_response(
FILE: galaxy/client/components/device_registry.py
class DeviceRegistry (line 18) | class DeviceRegistry:
method __init__ (line 24) | def __init__(self):
method register_device (line 29) | def register_device(
method get_device (line 64) | def get_device(self, device_id: str) -> Optional[AgentProfile]:
method get_all_devices (line 68) | def get_all_devices(self, connected: bool = False) -> Dict[str, AgentP...
method update_device_status (line 83) | def update_device_status(self, device_id: str, status: DeviceStatus) -...
method set_device_busy (line 88) | def set_device_busy(self, device_id: str, task_id: str) -> None:
method set_device_idle (line 100) | def set_device_idle(self, device_id: str) -> None:
method is_device_busy (line 111) | def is_device_busy(self, device_id: str) -> bool:
method get_current_task (line 122) | def get_current_task(self, device_id: str) -> Optional[str]:
method increment_connection_attempts (line 133) | def increment_connection_attempts(self, device_id: str) -> int:
method reset_connection_attempts (line 140) | def reset_connection_attempts(self, device_id: str) -> None:
method update_heartbeat (line 146) | def update_heartbeat(self, device_id: str) -> None:
method set_device_capabilities (line 151) | def set_device_capabilities(
method get_device_capabilities (line 165) | def get_device_capabilities(self, device_id: str) -> Dict[str, Any]:
method get_connected_devices (line 169) | def get_connected_devices(self) -> List[str]:
method is_device_registered (line 177) | def is_device_registered(self, device_id: str) -> bool:
method remove_device (line 181) | def remove_device(self, device_id: str) -> bool:
method update_device_system_info (line 189) | def update_device_system_info(
method get_device_system_info (line 258) | def get_device_system_info(self, device_id: str) -> Optional[Dict[str,...
FILE: galaxy/client/components/heartbeat_manager.py
class HeartbeatManager (line 21) | class HeartbeatManager:
method __init__ (line 27) | def __init__(
method start_heartbeat (line 41) | def start_heartbeat(self, device_id: str) -> None:
method stop_heartbeat (line 49) | def stop_heartbeat(self, device_id: str) -> None:
method _heartbeat_loop (line 61) | async def _heartbeat_loop(self, device_id: str) -> None:
method handle_heartbeat_response (line 90) | def handle_heartbeat_response(self, device_id: str) -> None:
method stop_all_heartbeats (line 95) | def stop_all_heartbeats(self) -> None:
FILE: galaxy/client/components/message_processor.py
class MessageProcessor (line 27) | class MessageProcessor:
method __init__ (line 37) | def __init__(
method set_connection_manager (line 60) | def set_connection_manager(
method set_disconnection_handler (line 74) | def set_disconnection_handler(self, handler: callable) -> None:
method start_message_handler (line 86) | def start_message_handler(
method stop_message_handler (line 105) | def stop_message_handler(self, device_id: str) -> None:
method _handle_device_messages (line 121) | async def _handle_device_messages(
method _process_server_message (line 216) | async def _process_server_message(
method _handle_task_completion (line 287) | async def _handle_task_completion(
method _handle_error_message (line 343) | async def _handle_error_message(
method _handle_command_message (line 358) | async def _handle_command_message(
method _handle_device_info_response (line 387) | async def _handle_device_info_response(
method _process_device_info_response (line 437) | async def _process_device_info_response(self, device_id: str, results:...
method _handle_disconnection (line 468) | async def _handle_disconnection(self, device_id: str) -> None:
method stop_all_handlers (line 498) | def stop_all_handlers(self) -> None:
FILE: galaxy/client/components/task_queue_manager.py
class TaskQueueManager (line 19) | class TaskQueueManager:
method __init__ (line 25) | def __init__(self):
method enqueue_task (line 34) | def enqueue_task(self, device_id: str, task_request: TaskRequest) -> a...
method dequeue_task (line 65) | def dequeue_task(self, device_id: str) -> Optional[TaskRequest]:
method peek_next_task (line 82) | def peek_next_task(self, device_id: str) -> Optional[TaskRequest]:
method get_queue_size (line 93) | def get_queue_size(self, device_id: str) -> int:
method has_queued_tasks (line 99) | def has_queued_tasks(self, device_id: str) -> bool:
method complete_task (line 103) | def complete_task(self, device_id: str, task_id: str, result: any) -> ...
method fail_task (line 121) | def fail_task(self, device_id: str, task_id: str, exception: Exception...
method cancel_all_tasks (line 141) | def cancel_all_tasks(self, device_id: str) -> None:
method get_pending_task_ids (line 162) | def get_pending_task_ids(self, device_id: str) -> List[str]:
method get_queued_task_ids (line 168) | def get_queued_task_ids(self, device_id: str) -> List[str]:
FILE: galaxy/client/components/types.py
class DeviceStatus (line 17) | class DeviceStatus(Enum):
class AgentProfile (line 30) | class AgentProfile:
class TaskRequest (line 46) | class TaskRequest:
class DeviceEventHandler (line 58) | class DeviceEventHandler(ABC):
method on_device_connected (line 62) | async def on_device_connected(
method on_device_disconnected (line 69) | async def on_device_disconnected(self, device_id: str) -> None:
method on_task_completed (line 74) | async def on_task_completed(
FILE: galaxy/client/config_loader.py
class DeviceConfig (line 26) | class DeviceConfig:
class ConstellationConfig (line 39) | class ConstellationConfig:
method from_file (line 49) | def from_file(cls, config_path: str) -> "ConstellationConfig":
method from_json (line 63) | def from_json(cls, config_path: str) -> "ConstellationConfig":
method from_yaml (line 103) | def from_yaml(cls, config_path: str) -> "ConstellationConfig":
method from_args (line 170) | def from_args(cls, args: argparse.Namespace) -> "ConstellationConfig":
method from_env (line 223) | def from_env(cls) -> "ConstellationConfig":
method to_file (line 262) | def to_file(self, config_path: str) -> None:
method to_json (line 274) | def to_json(self, config_path: str) -> None:
method to_yaml (line 312) | def to_yaml(self, config_path: str) -> None:
method add_device (line 361) | def add_device(
method remove_device (line 387) | def remove_device(self, device_id: str) -> bool:
method get_device_config (line 400) | def get_device_config(self, device_id: str) -> Optional[DeviceConfig]:
method create_sample_config (line 413) | def create_sample_config(cls, file_path: str) -> None:
function create_sample_config (line 422) | def create_sample_config(config_path: str) -> None:
function setup_argument_parser (line 472) | def setup_argument_parser() -> argparse.ArgumentParser:
FILE: galaxy/client/constellation_client.py
class ConstellationClient (line 18) | class ConstellationClient:
method __init__ (line 33) | def __init__(
method initialize (line 59) | async def initialize(self) -> Dict[str, bool]:
method register_device_from_config (line 91) | async def register_device_from_config(self, device_config: DeviceConfi...
method register_device (line 107) | async def register_device(
method connect_device (line 125) | async def connect_device(self, device_id: str) -> bool:
method disconnect_device (line 129) | async def disconnect_device(self, device_id: str) -> bool:
method connect_all_devices (line 133) | async def connect_all_devices(self) -> Dict[str, bool]:
method disconnect_all_devices (line 137) | async def disconnect_all_devices(self) -> None:
method ensure_devices_connected (line 141) | async def ensure_devices_connected(self) -> Dict[str, bool]:
method get_device_status (line 151) | def get_device_status(self, device_id: Optional[str] = None) -> Dict[s...
method get_connected_devices (line 161) | def get_connected_devices(self) -> List[str]:
method get_constellation_info (line 165) | def get_constellation_info(self) -> Dict[str, Any]:
method validate_config (line 179) | def validate_config(
method get_config_summary (line 201) | def get_config_summary(self) -> Dict[str, Any]:
method add_device_to_config (line 222) | async def add_device_to_config(
method shutdown (line 251) | async def shutdown(self) -> None:
function create_constellation_client (line 264) | async def create_constellation_client(
FILE: galaxy/client/demo_device_events.py
class DeviceEventMonitor (line 23) | class DeviceEventMonitor(IEventObserver):
method __init__ (line 26) | def __init__(self, name: str = "DeviceMonitor"):
method on_event (line 30) | async def on_event(self, event: Any) -> None:
method _handle_device_event (line 36) | async def _handle_device_event(self, event: DeviceEvent) -> None:
method _get_status_icon (line 84) | def _get_status_icon(status: str) -> str:
class DeviceStatisticsMonitor (line 97) | class DeviceStatisticsMonitor(IEventObserver):
method __init__ (line 100) | def __init__(self):
method on_event (line 106) | async def on_event(self, event: Any) -> None:
method print_statistics (line 118) | def print_statistics(self) -> None:
function demo_device_events (line 130) | async def demo_device_events():
FILE: galaxy/client/device_manager.py
class ConstellationDeviceManager (line 33) | class ConstellationDeviceManager:
method __init__ (line 45) | def __init__(
method _get_device_registry_snapshot (line 87) | def _get_device_registry_snapshot(self) -> Dict[str, Dict[str, Any]]:
method _publish_device_event (line 116) | async def _publish_device_event(
method register_device (line 174) | async def register_device(
method connect_device (line 221) | async def connect_device(
method disconnect_device (line 359) | async def disconnect_device(self, device_id: str) -> None:
method _handle_device_disconnection (line 380) | async def _handle_device_disconnection(self, device_id: str) -> None:
method _schedule_reconnection (line 454) | def _schedule_reconnection(self, device_id: str) -> None:
method _reconnect_device (line 461) | async def _reconnect_device(self, device_id: str) -> None:
method assign_task_to_device (line 543) | async def assign_task_to_device(
method _execute_task_on_device (line 600) | async def _execute_task_on_device(
method _process_next_queued_task (line 735) | async def _process_next_queued_task(self, device_id: str) -> None:
method get_device_info (line 751) | def get_device_info(self, device_id: str) -> Optional[AgentProfile]:
method get_connected_devices (line 755) | def get_connected_devices(self) -> List[str]:
method get_device_capabilities (line 759) | def get_device_capabilities(self, device_id: str) -> Dict[str, Any]:
method get_device_system_info (line 763) | def get_device_system_info(self, device_id: str) -> Optional[Dict[str,...
method get_all_devices (line 773) | def get_all_devices(self, connected=False) -> Dict[str, AgentProfile]:
method get_device_status (line 781) | def get_device_status(self, device_id: str) -> Dict[str, Any]:
method get_task_queue_status (line 805) | def get_task_queue_status(self, device_id: str) -> Dict[str, Any]:
method ensure_devices_connected (line 821) | async def ensure_devices_connected(self) -> Dict[str, bool]:
method shutdown (line 878) | async def shutdown(self) -> None:
FILE: galaxy/client/support/client_config_manager.py
class ClientConfigManager (line 18) | class ClientConfigManager:
method __init__ (line 24) | def __init__(self, device_manager: ConstellationDeviceManager):
method initialize_from_config (line 33) | async def initialize_from_config(
method register_device_from_config (line 72) | async def register_device_from_config(self, device_config: DeviceConfi...
method add_device_to_config (line 94) | async def add_device_to_config(
method validate_config (line 161) | def validate_config(self, config: ConstellationConfig) -> Dict[str, Any]:
method _validate_device_config (line 221) | def _validate_device_config(self, device_config: DeviceConfig) -> Dict...
method get_config_summary (line 273) | def get_config_summary(self, config: ConstellationConfig) -> Dict[str,...
FILE: galaxy/client/support/status_manager.py
class StatusManager (line 18) | class StatusManager:
method __init__ (line 24) | def __init__(
method get_device_status (line 42) | def get_device_status(self, device_id: Optional[str] = None) -> Dict[s...
method _get_single_device_status (line 54) | def _get_single_device_status(self, device_id: str) -> Dict[str, Any]:
method _get_all_devices_status (line 87) | def _get_all_devices_status(self) -> Dict[str, Any]:
method get_connected_devices (line 99) | def get_connected_devices(self) -> List[str]:
method get_constellation_info (line 103) | def get_constellation_info(self) -> Dict[str, Any]:
method get_device_health_summary (line 127) | def get_device_health_summary(self) -> Dict[str, Any]:
method get_task_statistics (line 168) | def get_task_statistics(self) -> Dict[str, Any]:
method get_performance_metrics (line 187) | def get_performance_metrics(self) -> Dict[str, Any]:
method _calculate_average_connection_attempts (line 208) | def _calculate_average_connection_attempts(self) -> float:
method _calculate_overall_health_score (line 219) | def _calculate_overall_health_score(
method get_diagnostics_report (line 243) | def get_diagnostics_report(self) -> Dict[str, Any]:
FILE: galaxy/constellation/editor/command_history.py
class CommandHistory (line 15) | class CommandHistory:
method __init__ (line 23) | def __init__(self, max_history_size: int = 100):
method add_command (line 33) | def add_command(self, command: IUndoableCommand) -> None:
method can_undo (line 52) | def can_undo(self) -> bool:
method can_redo (line 64) | def can_redo(self) -> bool:
method undo (line 75) | def undo(self) -> Optional[IUndoableCommand]:
method redo (line 93) | def redo(self) -> Optional[IUndoableCommand]:
method clear (line 114) | def clear(self) -> None:
method get_history (line 119) | def get_history(self) -> List[IUndoableCommand]:
method get_current_command (line 127) | def get_current_command(self) -> Optional[IUndoableCommand]:
method get_undo_description (line 137) | def get_undo_description(self) -> Optional[str]:
method get_redo_description (line 147) | def get_redo_description(self) -> Optional[str]:
method size (line 159) | def size(self) -> int:
method current_index (line 164) | def current_index(self) -> int:
method __len__ (line 168) | def __len__(self) -> int:
method __str__ (line 172) | def __str__(self) -> str:
FILE: galaxy/constellation/editor/command_interface.py
class ICommand (line 14) | class ICommand(ABC):
method execute (line 23) | def execute(self) -> Any:
method can_execute (line 33) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 41) | def get_cannot_execute_reason(self) -> str:
method description (line 54) | def description(self) -> str:
class IUndoableCommand (line 63) | class IUndoableCommand(ICommand):
method undo (line 71) | def undo(self) -> Any:
method can_undo (line 81) | def can_undo(self) -> bool:
method is_executed (line 91) | def is_executed(self) -> bool:
class CommandExecutionError (line 100) | class CommandExecutionError(Exception):
method __init__ (line 103) | def __init__(
class CommandUndoError (line 114) | class CommandUndoError(Exception):
method __init__ (line 117) | def __init__(
FILE: galaxy/constellation/editor/command_invoker.py
class CommandInvoker (line 16) | class CommandInvoker:
method __init__ (line 24) | def __init__(self, enable_history: bool = True, max_history_size: int ...
method execute (line 35) | def execute(self, command: ICommand) -> Any:
method undo (line 67) | def undo(self) -> Optional[IUndoableCommand]:
method redo (line 78) | def redo(self) -> Optional[IUndoableCommand]:
method can_undo (line 89) | def can_undo(self) -> bool:
method can_redo (line 101) | def can_redo(self) -> bool:
method clear_history (line 113) | def clear_history(self) -> None:
method get_history (line 118) | def get_history(self) -> List[IUndoableCommand]:
method get_undo_description (line 128) | def get_undo_description(self) -> Optional[str]:
method get_redo_description (line 138) | def get_redo_description(self) -> Optional[str]:
method execution_count (line 149) | def execution_count(self) -> int:
method history_enabled (line 154) | def history_enabled(self) -> bool:
method history_size (line 159) | def history_size(self) -> int:
method enable_history (line 163) | def enable_history(self, enable: bool = True, max_history_size: int = ...
method __str__ (line 177) | def __str__(self) -> str:
FILE: galaxy/constellation/editor/command_registry.py
class CommandRegistry (line 13) | class CommandRegistry:
method __init__ (line 16) | def __init__(self):
method register (line 21) | def register(
method get_command (line 61) | def get_command(self, name: str) -> Optional[Type[ICommand]]:
method list_commands (line 70) | def list_commands(
method get_command_metadata (line 88) | def get_command_metadata(self, name: str) -> Optional[Dict[str, Any]]:
method is_registered (line 97) | def is_registered(self, name: str) -> bool:
method unregister (line 106) | def unregister(self, name: str) -> bool:
method clear (line 119) | def clear(self) -> None:
method create_command (line 124) | def create_command(self, name: str, *args, **kwargs) -> Optional[IComm...
method get_categories (line 142) | def get_categories(self) -> list[str]:
function register_command (line 158) | def register_command(
FILE: galaxy/constellation/editor/commands.py
class BaseConstellationCommand (line 21) | class BaseConstellationCommand(IUndoableCommand):
method __init__ (line 28) | def __init__(self, constellation: TaskConstellation, description: str):
method constellation (line 41) | def constellation(self) -> TaskConstellation:
method description (line 46) | def description(self) -> str:
method is_executed (line 51) | def is_executed(self) -> bool:
method _create_backup (line 55) | def _create_backup(self) -> None:
method _restore_backup (line 70) | def _restore_backup(self) -> None:
class AddTaskCommand (line 103) | class AddTaskCommand(BaseConstellationCommand):
method __init__ (line 106) | def __init__(self, constellation: TaskConstellation, task_data: dict):
method can_execute (line 118) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 124) | def get_cannot_execute_reason(self) -> str:
method execute (line 134) | def execute(self) -> TaskStar:
method can_undo (line 164) | def can_undo(self) -> bool:
method undo (line 168) | def undo(self) -> None:
class RemoveTaskCommand (line 191) | class RemoveTaskCommand(BaseConstellationCommand):
method __init__ (line 194) | def __init__(self, constellation: TaskConstellation, task_id: str):
method can_execute (line 206) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 215) | def get_cannot_execute_reason(self) -> str:
method execute (line 229) | def execute(self) -> str:
method can_undo (line 269) | def can_undo(self) -> bool:
method undo (line 273) | def undo(self) -> None:
class UpdateTaskCommand (line 296) | class UpdateTaskCommand(BaseConstellationCommand):
method __init__ (line 299) | def __init__(
method can_execute (line 314) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 319) | def get_cannot_execute_reason(self) -> str:
method execute (line 329) | def execute(self) -> TaskStar:
method can_undo (line 365) | def can_undo(self) -> bool:
method undo (line 369) | def undo(self) -> None:
class AddDependencyCommand (line 397) | class AddDependencyCommand(BaseConstellationCommand):
method __init__ (line 400) | def __init__(self, constellation: TaskConstellation, dependency_data: ...
method can_execute (line 415) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 424) | def get_cannot_execute_reason(self) -> str:
method execute (line 438) | def execute(self) -> TaskStarLine:
method can_undo (line 469) | def can_undo(self) -> bool:
method undo (line 473) | def undo(self) -> None:
class RemoveDependencyCommand (line 496) | class RemoveDependencyCommand(BaseConstellationCommand):
method __init__ (line 499) | def __init__(self, constellation: TaskConstellation, dependency_id: str):
method can_execute (line 510) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 517) | def get_cannot_execute_reason(self) -> str:
method execute (line 526) | def execute(self) -> str:
method can_undo (line 561) | def can_undo(self) -> bool:
method undo (line 565) | def undo(self) -> None:
class UpdateDependencyCommand (line 587) | class UpdateDependencyCommand(BaseConstellationCommand):
method __init__ (line 590) | def __init__(
method can_execute (line 608) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 613) | def get_cannot_execute_reason(self) -> str:
method execute (line 623) | def execute(self) -> TaskStarLine:
method can_undo (line 659) | def can_undo(self) -> bool:
method undo (line 663) | def undo(self) -> None:
class BuildConstellationCommand (line 691) | class BuildConstellationCommand(BaseConstellationCommand):
method __init__ (line 694) | def __init__(
method can_execute (line 712) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 716) | def get_cannot_execute_reason(self) -> str:
method execute (line 724) | def execute(self) -> TaskConstellation:
method can_undo (line 755) | def can_undo(self) -> bool:
method undo (line 759) | def undo(self) -> None:
class ClearConstellationCommand (line 778) | class ClearConstellationCommand(BaseConstellationCommand):
method __init__ (line 781) | def __init__(self, constellation: TaskConstellation):
method can_execute (line 789) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 793) | def get_cannot_execute_reason(self) -> str:
method execute (line 799) | def execute(self) -> TaskConstellation:
method can_undo (line 831) | def can_undo(self) -> bool:
method undo (line 835) | def undo(self) -> None:
class LoadConstellationCommand (line 854) | class LoadConstellationCommand(BaseConstellationCommand):
method __init__ (line 857) | def __init__(self, constellation: TaskConstellation, file_path: str):
method can_execute (line 867) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 873) | def get_cannot_execute_reason(self) -> str:
method execute (line 883) | def execute(self) -> TaskConstellation:
method can_undo (line 922) | def can_undo(self) -> bool:
method undo (line 926) | def undo(self) -> None:
class SaveConstellationCommand (line 945) | class SaveConstellationCommand(BaseConstellationCommand):
method __init__ (line 948) | def __init__(self, constellation: TaskConstellation, file_path: str):
method can_execute (line 960) | def can_execute(self) -> bool:
method get_cannot_execute_reason (line 964) | def get_cannot_execute_reason(self) -> str:
method execute (line 970) | def execute(self) -> str:
method can_undo (line 995) | def can_undo(self) -> bool:
method undo (line 999) | def undo(self) -> None:
FILE: galaxy/constellation/editor/constellation_editor.py
class ConstellationEditor (line 34) | class ConstellationEditor:
method __init__ (line 42) | def __init__(
method constellation (line 60) | def constellation(self) -> TaskConstellation:
method invoker (line 65) | def invoker(self) -> CommandInvoker:
method add_observer (line 69) | def add_observer(self, observer: callable) -> None:
method remove_observer (line 78) | def remove_observer(self, observer: callable) -> None:
method _notify_observers (line 87) | def _notify_observers(self, command: str, result: Any) -> None:
method add_task (line 97) | def add_task(self, task: Union[TaskStar, Dict[str, Any]]) -> TaskStar:
method create_and_add_task (line 115) | def create_and_add_task(
method remove_task (line 130) | def remove_task(self, task_id: str) -> str:
method update_task (line 143) | def update_task(self, task_id: str, **updates) -> TaskStar:
method get_task (line 157) | def get_task(self, task_id: str) -> Optional[TaskStar]:
method list_tasks (line 166) | def list_tasks(self) -> List[TaskStar]:
method add_dependency (line 176) | def add_dependency(
method create_and_add_dependency (line 196) | def create_and_add_dependency(
method remove_dependency (line 226) | def remove_dependency(self, dependency_id: str) -> str:
method update_dependency (line 239) | def update_dependency(self, dependency_id: str, **updates) -> TaskStar...
method get_dependency (line 253) | def get_dependency(self, dependency_id: str) -> Optional[TaskStarLine]:
method list_dependencies (line 262) | def list_dependencies(self) -> List[TaskStarLine]:
method get_task_dependencies (line 270) | def get_task_dependencies(self, task_id: str) -> List[TaskStarLine]:
method build_constellation (line 281) | def build_constellation(
method build_from_tasks_and_dependencies (line 298) | def build_from_tasks_and_dependencies(
method clear_constellation (line 320) | def clear_constellation(self) -> TaskConstellation:
method load_constellation (line 334) | def load_constellation(self, file_path: str) -> TaskConstellation:
method save_constellation (line 347) | def save_constellation(self, file_path: str) -> str:
method load_from_dict (line 360) | def load_from_dict(self, data: Dict[str, Any]) -> TaskConstellation:
method load_from_json_string (line 374) | def load_from_json_string(self, json_string: str) -> TaskConstellation:
method undo (line 387) | def undo(self) -> bool:
method redo (line 399) | def redo(self) -> bool:
method can_undo (line 411) | def can_undo(self) -> bool:
method can_redo (line 415) | def can_redo(self) -> bool:
method get_undo_description (line 419) | def get_undo_description(self) -> Optional[str]:
method get_redo_description (line 423) | def get_redo_description(self) -> Optional[str]:
method clear_history (line 427) | def clear_history(self) -> None:
method get_history (line 432) | def get_history(self) -> List[str]:
method validate_constellation (line 442) | def validate_constellation(self) -> tuple[bool, List[str]]:
method get_topological_order (line 450) | def get_topological_order(self) -> List[str]:
method has_cycles (line 459) | def has_cycles(self) -> bool:
method get_ready_tasks (line 463) | def get_ready_tasks(self) -> List[TaskStar]:
method get_statistics (line 467) | def get_statistics(self) -> Dict[str, Any]:
method batch_operations (line 482) | def batch_operations(self, operations: List[callable]) -> List[Any]:
method create_subgraph (line 498) | def create_subgraph(self, task_ids: List[str]) -> "ConstellationEditor":
method merge_constellation (line 532) | def merge_constellation(
method display_constellation (line 570) | def display_constellation(self, mode: str = "overview") -> None:
method list_available_commands (line 579) | def list_available_commands(
method get_command_metadata (line 590) | def get_command_metadata(self, command_name: str) -> Optional[Dict[str...
method execute_command_by_name (line 599) | def execute_command_by_name(self, command_name: str, *args, **kwargs) ...
method get_command_categories (line 616) | def get_command_categories(self) -> List[str]:
method __str__ (line 624) | def __str__(self) -> str:
method __repr__ (line 634) | def __repr__(self) -> str:
FILE: galaxy/constellation/enums.py
class TaskStatus (line 14) | class TaskStatus(Enum):
class DependencyType (line 27) | class DependencyType(Enum):
class ConstellationState (line 40) | class ConstellationState(Enum):
class TaskPriority (line 54) | class TaskPriority(Enum):
class DeviceType (line 65) | class DeviceType(Enum):
FILE: galaxy/constellation/orchestrator/constellation_manager.py
class ConstellationManager (line 19) | class ConstellationManager:
method __init__ (line 30) | def __init__(
method set_device_manager (line 48) | def set_device_manager(self, device_manager: ConstellationDeviceManage...
method register_constellation (line 58) | def register_constellation(
method unregister_constellation (line 81) | def unregister_constellation(self, constellation_id: str) -> bool:
method get_constellation (line 101) | def get_constellation(self, constellation_id: str) -> Optional[TaskCon...
method list_constellations (line 110) | def list_constellations(self) -> List[Dict[str, Any]]:
method assign_devices_automatically (line 132) | async def assign_devices_automatically(
method _assign_round_robin (line 187) | async def _assign_round_robin(
method _assign_capability_match (line 212) | async def _assign_capability_match(
method _assign_load_balance (line 249) | async def _assign_load_balance(
method get_constellation_status (line 275) | async def get_constellation_status(
method get_available_devices (line 306) | async def get_available_devices(self) -> List[Dict[str, Any]]:
method _get_available_devices (line 314) | async def _get_available_devices(self) -> List[Dict[str, Any]]:
method validate_constellation_assignments (line 346) | def validate_constellation_assignments(
method get_task_device_info (line 375) | def get_task_device_info(
method reassign_task_device (line 410) | def reassign_task_device(
method clear_device_assignments (line 438) | def clear_device_assignments(self, constellation: TaskConstellation) -...
method get_device_utilization (line 459) | def get_device_utilization(
FILE: galaxy/constellation/orchestrator/orchestrator.py
class TaskConstellationOrchestrator (line 31) | class TaskConstellationOrchestrator:
method __init__ (line 40) | def __init__(
method set_device_manager (line 78) | def set_device_manager(self, device_manager: ConstellationDeviceManage...
method set_modification_synchronizer (line 87) | def set_modification_synchronizer(
method cancel_execution (line 99) | async def cancel_execution(self, constellation_id: str) -> bool:
method orchestrate_constellation (line 143) | async def orchestrate_constellation(
method _validate_and_prepare_constellation (line 226) | async def _validate_and_prepare_constellation(
method _assign_devices_to_tasks (line 269) | async def _assign_devices_to_tasks(
method _validate_existing_device_assignments (line 298) | def _validate_existing_device_assignments(
method _start_constellation_execution (line 357) | async def _start_constellation_execution(
method _run_execution_loop (line 394) | async def _run_execution_loop(self, constellation: TaskConstellation) ...
method _sync_constellation_modifications (line 434) | async def _sync_constellation_modifications(
method _schedule_ready_tasks (line 468) | async def _schedule_ready_tasks(
method _wait_for_task_completion (line 484) | async def _wait_for_task_completion(self) -> None:
method _cleanup_completed_tasks (line 499) | async def _cleanup_completed_tasks(self, done_futures: set) -> None:
method _wait_for_all_tasks (line 515) | async def _wait_for_all_tasks(self) -> None:
method _finalize_constellation_execution (line 538) | async def _finalize_constellation_execution(
method _handle_orchestration_failure (line 580) | async def _handle_orchestration_failure(
method _cleanup_constellation (line 593) | async def _cleanup_constellation(self, constellation: TaskConstellatio...
method _execute_task_with_events (line 603) | async def _execute_task_with_events(
method execute_single_task (line 698) | async def execute_single_task(
method get_constellation_status (line 726) | async def get_constellation_status(
method get_available_devices (line 739) | async def get_available_devices(self) -> List[Dict[str, Any]]:
method assign_devices_automatically (line 747) | async def assign_devices_automatically(
FILE: galaxy/constellation/task_constellation.py
class TaskConstellation (line 31) | class TaskConstellation(IConstellation):
method __init__ (line 45) | def __init__(
method constellation_id (line 77) | def constellation_id(self) -> str:
method name (line 82) | def name(self) -> str:
method name (line 87) | def name(self, value: str) -> None:
method state (line 93) | def state(self) -> ConstellationState:
method tasks (line 98) | def tasks(self) -> Dict[str, TaskStar]:
method dependencies (line 103) | def dependencies(self) -> Dict[str, TaskStarLine]:
method task_count (line 108) | def task_count(self) -> int:
method dependency_count (line 113) | def dependency_count(self) -> int:
method created_at (line 118) | def created_at(self) -> datetime:
method updated_at (line 123) | def updated_at(self) -> datetime:
method execution_start_time (line 128) | def execution_start_time(self) -> Optional[datetime]:
method execution_end_time (line 133) | def execution_end_time(self) -> Optional[datetime]:
method execution_duration (line 138) | def execution_duration(self) -> Optional[float]:
method metadata (line 147) | def metadata(self) -> Dict[str, Any]:
method update_metadata (line 151) | def update_metadata(self, metadata: Dict[str, Any]) -> None:
method add_task (line 156) | def add_task(self, task: TaskStar) -> None:
method remove_task (line 172) | def remove_task(self, task_id: str) -> None:
method get_task (line 201) | def get_task(self, task_id: str) -> Optional[TaskStar]:
method add_dependency (line 210) | def add_dependency(self, dependency: TaskStarLine) -> None:
method remove_dependency (line 244) | def remove_dependency(self, dependency_id: str) -> None:
method get_dependency (line 270) | def get_dependency(self, dependency_id: str) -> Optional[TaskStarLine]:
method get_ready_tasks (line 279) | def get_ready_tasks(self) -> List[TaskStar]:
method get_running_tasks (line 296) | def get_running_tasks(self) -> List[TaskStar]:
method get_completed_tasks (line 302) | def get_completed_tasks(self) -> List[TaskStar]:
method get_failed_tasks (line 308) | def get_failed_tasks(self) -> List[TaskStar]:
method get_pending_tasks (line 314) | def get_pending_tasks(self) -> List[TaskStar]:
method get_all_tasks (line 320) | def get_all_tasks(self) -> List[TaskStar]:
method get_all_dependencies (line 324) | def get_all_dependencies(self) -> List[TaskStarLine]:
method get_task_dependencies (line 328) | def get_task_dependencies(self, task_id: str) -> List[TaskStarLine]:
method get_modifiable_tasks (line 332) | def get_modifiable_tasks(self) -> List[TaskStar]:
method get_modifiable_dependencies (line 343) | def get_modifiable_dependencies(self) -> List[TaskStarLine]:
method is_task_modifiable (line 360) | def is_task_modifiable(self, task_id: str) -> bool:
method is_dependency_modifiable (line 372) | def is_dependency_modifiable(self, dependency_id: str) -> bool:
method is_complete (line 387) | def is_complete(self) -> bool:
method update_state (line 391) | def update_state(self) -> None:
method start_task (line 420) | def start_task(self, task_id: str) -> None:
method mark_task_completed (line 436) | def mark_task_completed(
method validate_dag (line 483) | def validate_dag(self) -> Tuple[bool, List[str]]:
method get_topological_order (line 508) | def get_topological_order(self) -> List[str]:
method get_longest_path (line 549) | def get_longest_path(self) -> Tuple[int, List[str]]:
method get_max_width (line 610) | def get_max_width(self) -> int:
method get_critical_path_length_with_time (line 651) | def get_critical_path_length_with_time(self) -> Tuple[float, List[str]]:
method get_total_work (line 724) | def get_total_work(self) -> float:
method get_parallelism_metrics (line 737) | def get_parallelism_metrics(self) -> Dict[str, Any]:
method get_statistics (line 799) | def get_statistics(self) -> Dict[str, Any]:
method to_dict (line 846) | def to_dict(self) -> Dict[str, Any]:
method _parse_constellation_state (line 885) | def _parse_constellation_state(state_value: Any) -> ConstellationState:
method from_dict (line 909) | def from_dict(cls, data: Dict[str, Any]) -> "TaskConstellation":
method to_json (line 953) | def to_json(self, save_path: Optional[str] = None) -> str:
method _ensure_json_serializable (line 994) | def _ensure_json_serializable(self, data: Any) -> Any:
method from_json (line 1058) | def from_json(
method from_basemodel (line 1105) | def from_basemodel(cls, schema: "TaskConstellationSchema") -> "TaskCon...
method to_basemodel (line 1121) | def to_basemodel(self) -> "TaskConstellationSchema":
method _are_dependencies_satisfied (line 1133) | def _are_dependencies_satisfied(self, task_id: str) -> bool:
method _would_create_cycle (line 1158) | def _would_create_cycle(self, from_task_id: str, to_task_id: str) -> b...
method has_cycle (line 1181) | def has_cycle(self) -> bool:
method start_execution (line 1189) | def start_execution(self) -> None:
method complete_execution (line 1196) | def complete_execution(self) -> None:
method display_dag (line 1202) | def display_dag(self, mode: str = "overview") -> None:
method __str__ (line 1224) | def __str__(self) -> str:
method __repr__ (line 1228) | def __repr__(self) -> str:
FILE: galaxy/constellation/task_star.py
class TaskStar (line 28) | class TaskStar(ITask):
method __init__ (line 42) | def __init__(
method task_id (line 115) | def task_id(self) -> TaskId:
method name (line 120) | def name(self) -> str:
method name (line 125) | def name(self, value: str) -> None:
method description (line 138) | def description(self) -> str:
method description (line 143) | def description(self, value: str) -> None:
method tips (line 158) | def tips(self) -> List[str]:
method tips (line 163) | def tips(self, value: List[str]) -> None:
method description (line 176) | def description(self, value: str) -> None:
method execute (line 190) | async def execute(
method validate (line 258) | def validate(self) -> bool:
method get_validation_errors (line 296) | def get_validation_errors(self) -> List[str]:
method task_description (line 306) | def task_description(self) -> str:
method task_description (line 311) | def task_description(self, value: str) -> None:
method target_device_id (line 316) | def target_device_id(self) -> Optional[str]:
method target_device_id (line 321) | def target_device_id(self, value: Optional[str]) -> None:
method device_type (line 331) | def device_type(self) -> Optional[DeviceType]:
method device_type (line 336) | def device_type(self, value: Optional[DeviceType]) -> None:
method priority (line 346) | def priority(self) -> TaskPriority:
method priority (line 351) | def priority(self, value: TaskPriority) -> None:
method status (line 359) | def status(self) -> TaskStatus:
method result (line 364) | def result(self) -> Optional[Any]:
method error (line 369) | def error(self) -> Optional[Exception]:
method execution_start_time (line 374) | def execution_start_time(self) -> Optional[datetime]:
method execution_end_time (line 379) | def execution_end_time(self) -> Optional[datetime]:
method execution_duration (line 384) | def execution_duration(self) -> Optional[float]:
method created_at (line 393) | def created_at(self) -> datetime:
method updated_at (line 398) | def updated_at(self) -> datetime:
method is_terminal (line 403) | def is_terminal(self) -> bool:
method is_ready_to_execute (line 412) | def is_ready_to_execute(self) -> bool:
method task_data (line 417) | def task_data(self) -> Dict[str, Any]:
method update_task_data (line 421) | def update_task_data(self, data: Dict[str, Any]) -> None:
method start_execution (line 434) | def start_execution(self) -> None:
method complete_with_success (line 454) | def complete_with_success(self, result: Any) -> None:
method complete_with_failure (line 471) | def complete_with_failure(self, error: Exception) -> None:
method cancel (line 488) | def cancel(self) -> None:
method should_retry (line 496) | def should_retry(self) -> bool:
method retry (line 503) | def retry(self) -> None:
method add_dependency (line 519) | def add_dependency(self, dependency_task_id: TaskId) -> None:
method remove_dependency (line 527) | def remove_dependency(self, dependency_task_id: TaskId) -> None:
method add_dependent (line 535) | def add_dependent(self, dependent_task_id: TaskId) -> None:
method remove_dependent (line 543) | def remove_dependent(self, dependent_task_id: TaskId) -> None:
method to_request_string (line 551) | def to_request_string(self):
method to_dict (line 562) | def to_dict(self) -> Dict[str, Any]:
method _serialize_result (line 602) | def _serialize_result(self, result: Any) -> Any:
method _serialize_task_data (line 654) | def _serialize_task_data(self, task_data: Dict[str, Any]) -> Dict[str,...
method _parse_priority (line 672) | def _parse_priority(priority_value: Any) -> TaskPriority:
method _parse_device_type (line 700) | def _parse_device_type(device_type_value: Any) -> Optional[DeviceType]:
method _parse_status (line 727) | def _parse_status(status_value: Any) -> TaskStatus:
method from_dict (line 751) | def from_dict(cls, data: Dict[str, Any]) -> "TaskStar":
method from_basemodel (line 799) | def from_basemodel(cls, schema: "TaskStarSchema") -> "TaskStar":
method to_basemodel (line 815) | def to_basemodel(self) -> "TaskStarSchema":
method from_json (line 828) | def from_json(
method to_json (line 874) | def to_json(self, save_path: Optional[str] = None) -> str:
method _ensure_json_serializable (line 905) | def _ensure_json_serializable(self, data: Dict[str, Any]) -> Dict[str,...
method __str__ (line 941) | def __str__(self) -> str:
method __repr__ (line 945) | def __repr__(self) -> str:
FILE: galaxy/constellation/task_star_line.py
class TaskStarLine (line 22) | class TaskStarLine(IDependency):
method __init__ (line 35) | def __init__(
method line_id (line 73) | def line_id(self) -> str:
method from_task_id (line 78) | def from_task_id(self) -> str:
method to_task_id (line 83) | def to_task_id(self) -> str:
method source_task_id (line 88) | def source_task_id(self) -> str:
method target_task_id (line 93) | def target_task_id(self) -> str:
method dependency_type (line 98) | def dependency_type(self) -> DependencyType:
method dependency_type (line 103) | def dependency_type(self, value: DependencyType) -> None:
method condition_description (line 112) | def condition_description(self) -> str:
method condition_description (line 117) | def condition_description(self, value: str) -> None:
method is_satisfied (line 122) | def is_satisfied(self, completed_tasks: Optional[List[str]] = None) ->...
method last_evaluation_result (line 135) | def last_evaluation_result(self) -> Optional[bool]:
method last_evaluation_time (line 140) | def last_evaluation_time(self) -> Optional[datetime]:
method created_at (line 145) | def created_at(self) -> datetime:
method updated_at (line 150) | def updated_at(self) -> datetime:
method metadata (line 155) | def metadata(self) -> Dict[str, Any]:
method update_metadata (line 159) | def update_metadata(self, metadata: Dict[str, Any]) -> None:
method set_condition_evaluator (line 169) | def set_condition_evaluator(self, evaluator: Callable[[Any], bool]) ->...
method evaluate_condition (line 182) | def evaluate_condition(self, prerequisite_result: Any) -> bool:
method mark_satisfied (line 220) | def mark_satisfied(self) -> None:
method reset_satisfaction (line 227) | def reset_satisfaction(self) -> None:
method to_dict (line 234) | def to_dict(self) -> Dict[str, Any]:
method _parse_dependency_type (line 259) | def _parse_dependency_type(dep_type_value: Any) -> DependencyType:
method from_dict (line 283) | def from_dict(cls, data: Dict[str, Any]) -> "TaskStarLine":
method from_basemodel (line 318) | def from_basemodel(cls, schema: "TaskStarLineSchema") -> "TaskStarLine":
method to_basemodel (line 334) | def to_basemodel(self) -> "TaskStarLineSchema":
method to_json (line 346) | def to_json(self, save_path: Optional[str] = None) -> str:
method _ensure_json_serializable (line 375) | def _ensure_json_serializable(self, data: Dict[str, Any]) -> Dict[str,...
method from_json (line 412) | def from_json(
method create_unconditional (line 459) | def create_unconditional(
method create_success_only (line 481) | def create_success_only(
method create_conditional (line 503) | def create_conditional(
method __str__ (line 527) | def __str__(self) -> str:
method __repr__ (line 531) | def __repr__(self) -> str:
FILE: galaxy/core/di_container.py
class LifecycleScope (line 31) | class LifecycleScope(Enum):
class DependencyInjectionError (line 39) | class DependencyInjectionError(GalaxyFrameworkError):
class ServiceDescriptor (line 45) | class ServiceDescriptor:
method __init__ (line 48) | def __init__(
class IDependencyContainer (line 78) | class IDependencyContainer(ABC):
method register_singleton (line 82) | def register_singleton(
method register_transient (line 100) | def register_transient(
method register_scoped (line 116) | def register_scoped(
method resolve (line 132) | def resolve(self, service_type: Type[T]) -> T:
method try_resolve (line 142) | def try_resolve(self, service_type: Type[T]) -> Optional[T]:
class DependencyContainer (line 152) | class DependencyContainer(IDependencyContainer):
method __init__ (line 160) | def __init__(self):
method register_singleton (line 168) | def register_singleton(
method register_transient (line 196) | def register_transient(
method register_scoped (line 218) | def register_scoped(
method resolve (line 240) | def resolve(self, service_type: Type[T]) -> T:
method try_resolve (line 255) | def try_resolve(self, service_type: Type[T]) -> Optional[T]:
method _create_instance (line 293) | def _create_instance(self, descriptor: ServiceDescriptor) -> Optional[...
method _create_with_constructor_injection (line 337) | def _create_with_constructor_injection(self, implementation_type: Type...
method _call_with_injection (line 383) | def _call_with_injection(self, factory: Callable[..., T]) -> T:
method clear_scoped (line 422) | def clear_scoped(self) -> None:
method get_registered_services (line 427) | def get_registered_services(self) -> List[Type]:
method is_registered (line 435) | def is_registered(self, service_type: Type) -> bool:
function get_container (line 449) | def get_container() -> DependencyContainer:
function set_container (line 461) | def set_container(container: DependencyContainer) -> None:
function resolve (line 471) | def resolve(service_type: Type[T]) -> T:
function try_resolve (line 481) | def try_resolve(service_type: Type[T]) -> Optional[T]:
function injectable (line 492) | def injectable(
FILE: galaxy/core/events.py
class EventType (line 16) | class EventType(Enum):
class Event (line 50) | class Event:
class TaskEvent (line 65) | class TaskEvent(Event):
class ConstellationEvent (line 80) | class ConstellationEvent(Event):
class AgentEvent (line 94) | class AgentEvent(Event):
class DeviceEvent (line 109) | class DeviceEvent(Event):
class IEventObserver (line 123) | class IEventObserver(ABC):
method on_event (line 132) | async def on_event(self, event: Event) -> None:
class IEventPublisher (line 145) | class IEventPublisher(ABC):
method subscribe (line 154) | def subscribe(
method unsubscribe (line 170) | def unsubscribe(self, observer: IEventObserver) -> None:
method publish_event (line 183) | async def publish_event(self, event: Event) -> None:
class EventBus (line 196) | class EventBus(IEventPublisher):
method __init__ (line 204) | def __init__(self):
method subscribe (line 217) | def subscribe(
method unsubscribe (line 242) | def unsubscribe(self, observer: IEventObserver) -> None:
method publish_event (line 256) | async def publish_event(self, event: Event) -> None:
function get_event_bus (line 290) | def get_event_bus() -> EventBus:
FILE: galaxy/core/interfaces.py
class ITask (line 30) | class ITask(ABC):
method task_id (line 35) | def task_id(self) -> TaskId:
method name (line 41) | def name(self) -> str:
method description (line 47) | def description(self) -> str:
method execute (line 52) | async def execute(
method validate (line 64) | def validate(self) -> bool:
class ITaskFactory (line 73) | class ITaskFactory(ABC):
method create_task (line 77) | def create_task(
method supports_task_type (line 96) | def supports_task_type(self, task_type: str) -> bool:
class IDependency (line 107) | class IDependency(ABC):
method source_task_id (line 112) | def source_task_id(self) -> TaskId:
method target_task_id (line 118) | def target_task_id(self) -> TaskId:
method dependency_type (line 124) | def dependency_type(self) -> str:
method is_satisfied (line 129) | def is_satisfied(self, completed_tasks: List[TaskId]) -> bool:
class IDependencyResolver (line 139) | class IDependencyResolver(ABC):
method get_ready_tasks (line 143) | def get_ready_tasks(
method validate_dependencies (line 160) | def validate_dependencies(
class IConstellation (line 174) | class IConstellation(ABC):
method constellation_id (line 179) | def constellation_id(self) -> ConstellationId:
method name (line 185) | def name(self) -> str:
method tasks (line 191) | def tasks(self) -> Dict[TaskId, ITask]:
method dependencies (line 197) | def dependencies(self) -> List[IDependency]:
method add_task (line 202) | def add_task(self, task: ITask) -> None:
method add_dependency (line 211) | def add_dependency(self, dependency: IDependency) -> None:
method get_ready_tasks (line 220) | def get_ready_tasks(
class IConstellationBuilder (line 232) | class IConstellationBuilder(ABC):
method create_constellation (line 236) | def create_constellation(self, name: str) -> IConstellation:
method add_task (line 246) | def add_task(self, constellation: IConstellation, task: ITask) -> ICon...
method add_dependency (line 257) | def add_dependency(
class ITaskExecutor (line 277) | class ITaskExecutor(ABC):
method execute_task (line 281) | async def execute_task(
method can_execute (line 294) | def can_execute(self, task: ITask) -> bool:
class IConstellationExecutor (line 304) | class IConstellationExecutor(ABC):
method execute_constellation (line 308) | async def execute_constellation(
method pause_execution (line 327) | async def pause_execution(self, constellation_id: ConstellationId) -> ...
method resume_execution (line 337) | async def resume_execution(self, constellation_id: ConstellationId) ->...
method cancel_execution (line 347) | async def cancel_execution(self, constellation_id: ConstellationId) ->...
class IDevice (line 358) | class IDevice(ABC):
method device_id (line 363) | def device_id(self) -> DeviceId:
method device_type (line 369) | def device_type(self) -> str:
method capabilities (line 375) | def capabilities(self) -> List[str]:
method is_connected (line 381) | def is_connected(self) -> bool:
method connect (line 386) | async def connect(self) -> bool:
method disconnect (line 395) | async def disconnect(self) -> bool:
method execute_task (line 404) | async def execute_task(self, task: ITask) -> ExecutionResult:
class IDeviceRegistry (line 414) | class IDeviceRegistry(ABC):
method register_device (line 418) | async def register_device(self, device: IDevice) -> bool:
method unregister_device (line 428) | async def unregister_device(self, device_id: DeviceId) -> bool:
method get_device (line 438) | async def get_device(self, device_id: DeviceId) -> Optional[IDevice]:
method get_available_devices (line 448) | async def get_available_devices(
class IDeviceSelector (line 460) | class IDeviceSelector(ABC):
method select_device (line 464) | async def select_device(
class IRequestProcessor (line 482) | class IRequestProcessor(ABC):
method process_creation (line 486) | async def process_creation(
class IResultProcessor (line 498) | class IResultProcessor(ABC):
method process_editing (line 502) | async def process_editing(
class IConstellationUpdater (line 515) | class IConstellationUpdater(ABC):
method should_update (line 519) | async def should_update(
method update_constellation (line 532) | async def update_constellation(
class ISessionManager (line 550) | class ISessionManager(ABC):
method create_session (line 554) | async def create_session(
method get_session (line 571) | async def get_session(self, session_id: SessionId) -> Optional["ISessi...
method end_session (line 581) | async def end_session(self, session_id: SessionId) -> bool:
class ISession (line 591) | class ISession(ABC):
method session_id (line 596) | def session_id(self) -> SessionId:
method is_active (line 602) | def is_active(self) -> bool:
method process_request (line 607) | async def process_request(self, request: str) -> ConstellationResult:
method get_status (line 617) | async def get_status(self) -> Dict[str, Any]:
class IMetricsCollector (line 627) | class IMetricsCollector(ABC):
method record_task_execution (line 631) | def record_task_execution(self, result: ExecutionResult) -> None:
method record_constellation_execution (line 640) | def record_constellation_execution(self, result: ConstellationResult) ...
method get_metrics (line 649) | def get_metrics(self) -> Dict[str, Any]:
class IEventLogger (line 658) | class IEventLogger(ABC):
method log_event (line 662) | def log_event(
method get_events (line 678) | def get_events(
FILE: galaxy/core/types.py
class TaskStatus (line 38) | class TaskStatus(Enum):
class ConstellationState (line 46) | class ConstellationState(Enum):
class TaskPriority (line 54) | class TaskPriority(Enum):
class DeviceType (line 60) | class DeviceType(Enum):
class DependencyType (line 69) | class DependencyType(Enum):
class ExecutionResult (line 97) | class ExecutionResult:
method execution_time (line 109) | def execution_time(self) -> Optional[float]:
method is_successful (line 116) | def is_successful(self) -> bool:
class ConstellationResult (line 122) | class ConstellationResult:
method execution_time (line 133) | def execution_time(self) -> Optional[float]:
method success_rate (line 140) | def success_rate(self) -> float:
class TaskConfiguration (line 152) | class TaskConfiguration:
class ConstellationConfiguration (line 163) | class ConstellationConfiguration:
class DeviceConfiguration (line 174) | class DeviceConfiguration:
class IExecutable (line 186) | class IExecutable(Protocol):
method execute (line 189) | async def execute(self, context: Optional[TContext] = None) -> Executi...
class IConfigurable (line 195) | class IConfigurable(Protocol):
method configure (line 198) | def configure(self, config: Dict[str, Any]) -> None:
class IObservable (line 204) | class IObservable(Protocol):
method add_observer (line 207) | def add_observer(self, observer: Callable[[Any], None]) -> None:
method remove_observer (line 211) | def remove_observer(self, observer: Callable[[Any], None]) -> None:
method notify_observers (line 215) | def notify_observers(self, event: Any) -> None:
class IValidatable (line 221) | class IValidatable(Protocol):
method validate (line 224) | def validate(self) -> bool:
method get_validation_errors (line 228) | def get_validation_errors(self) -> List[str]:
class ITaskProcessor (line 234) | class ITaskProcessor(ABC):
method process_task (line 238) | async def process_task(
class IConstellationManager (line 251) | class IConstellationManager(ABC):
method create_constellation (line 255) | async def create_constellation(
method execute_constellation (line 268) | async def execute_constellation(
class IDeviceManager (line 283) | class IDeviceManager(ABC):
method register_device (line 287) | async def register_device(self, device_config: DeviceConfiguration) ->...
method get_available_devices (line 297) | async def get_available_devices(
method assign_task_to_device (line 309) | async def assign_task_to_device(
class IAgentProcessor (line 322) | class IAgentProcessor(ABC):
method process_request (line 326) | async def process_request(
method process_result (line 339) | async def process_result(
class ITask (line 357) | class ITask(Protocol):
class IDependency (line 365) | class IDependency(Protocol):
class IConstellation (line 373) | class IConstellation(Protocol):
class GalaxyFrameworkError (line 383) | class GalaxyFrameworkError(Exception):
method __init__ (line 386) | def __init__(
class TaskExecutionError (line 398) | class TaskExecutionError(GalaxyFrameworkError):
method __init__ (line 401) | def __init__(
class ConstellationError (line 409) | class ConstellationError(GalaxyFrameworkError):
method __init__ (line 412) | def __init__(self, constellation_id: ConstellationId, message: str):
class DeviceError (line 417) | class DeviceError(GalaxyFrameworkError):
method __init__ (line 420) | def __init__(self, device_id: DeviceId, message: str):
class ConfigurationError (line 425) | class ConfigurationError(GalaxyFrameworkError):
class ValidationError (line 431) | class ValidationError(GalaxyFrameworkError):
method __init__ (line 434) | def __init__(self, message: str, validation_errors: List[str]):
class Statistics (line 441) | class Statistics:
method update_from_result (line 451) | def update_from_result(self, result: ExecutionResult) -> None:
class ProcessingContext (line 474) | class ProcessingContext:
method to_dict (line 486) | def to_dict(self) -> Dict[str, Any]:
FILE: galaxy/galaxy.py
function parse_args (line 37) | def parse_args():
function galaxy_quick_start (line 139) | async def galaxy_quick_start(
function galaxy_interactive (line 165) | async def galaxy_interactive(
function main (line 191) | async def main():
function run_demo_with_client (line 283) | async def run_demo_with_client(client: GalaxyClient):
function run_webui_mode (line 315) | async def run_webui_mode(client: GalaxyClient):
FILE: galaxy/galaxy_client.py
class GalaxyClient (line 40) | class GalaxyClient:
method __init__ (line 51) | def __init__(
method initialize (line 107) | async def initialize(self) -> None:
method process_request (line 150) | async def process_request(self, request: str) -> Dict[str, Any]:
method interactive_mode (line 317) | async def interactive_mode(self) -> None:
method _show_status (line 378) | def _show_status(self) -> None:
method _save_result (line 391) | def _save_result(self, result: Dict[str, Any]) -> None:
method reset_session (line 427) | async def reset_session(self) -> Dict[str, Any]:
method create_next_session (line 468) | async def create_next_session(self) -> Dict[str, Any]:
method shutdown (line 538) | async def shutdown(self, force: bool = False) -> None:
FILE: galaxy/session/galaxy_session.py
class GalaxyRound (line 43) | class GalaxyRound(BaseRound):
method __init__ (line 48) | def __init__(
method run (line 71) | async def run(self) -> None:
method is_finished (line 136) | def is_finished(self):
method force_finish (line 153) | def force_finish(self) -> None:
method constellation (line 161) | def constellation(self) -> Optional[TaskConstellation]:
class GalaxySession (line 170) | class GalaxySession(BaseSession):
method __init__ (line 178) | def __init__(
method _init_context (line 247) | def _init_context(self) -> None:
method _init_agents (line 273) | def _init_agents(self) -> None:
method _setup_observers (line 279) | def _setup_observers(self) -> None:
method run (line 324) | async def run(self) -> None:
method is_error (line 386) | def is_error(self) -> bool:
method is_finished (line 411) | def is_finished(self) -> bool:
method create_new_round (line 430) | def create_new_round(self) -> Optional[GalaxyRound]:
method next_request (line 453) | def next_request(self) -> str:
method request_to_evaluate (line 464) | def request_to_evaluate(self) -> str:
method set_agent (line 472) | def set_agent(self, agent: ConstellationAgent) -> None:
method force_finish (line 480) | async def force_finish(self, reason: str = "Manual termination") -> None:
method request_cancellation (line 495) | async def request_cancellation(self) -> None:
method reset (line 521) | def reset(self) -> None:
method _cleanup_observers (line 565) | def _cleanup_observers(self) -> None:
method current_constellation (line 577) | def current_constellation(self) -> Optional[TaskConstellation]:
method agent (line 586) | def agent(self) -> ConstellationAgent:
method orchestrator (line 595) | def orchestrator(self) -> TaskConstellationOrchestrator:
method session_results (line 604) | def session_results(self) -> Dict[str, Any]:
FILE: galaxy/session/observers/agent_output_observer.py
class AgentOutputObserver (line 26) | class AgentOutputObserver(IEventObserver):
method __init__ (line 34) | def __init__(self, presenter_type: str = "rich"):
method on_event (line 45) | async def on_event(self, event: Event) -> None:
method _handle_agent_response (line 62) | async def _handle_agent_response(self, event: AgentEvent) -> None:
method _handle_agent_action (line 90) | async def _handle_agent_action(self, event: AgentEvent) -> None:
FILE: galaxy/session/observers/base_observer.py
class ConstellationProgressObserver (line 22) | class ConstellationProgressObserver(IEventObserver):
method __init__ (line 29) | def __init__(self, agent: ConstellationAgent):
method on_event (line 39) | async def on_event(self, event: Event) -> None:
method _handle_task_event (line 50) | async def _handle_task_event(self, event: TaskEvent) -> None:
method _handle_constellation_event (line 85) | async def _handle_constellation_event(self, event: ConstellationEvent)...
class SessionMetricsObserver (line 105) | class SessionMetricsObserver(IEventObserver):
method __init__ (line 110) | def __init__(self, session_id: str, logger: Optional[logging.Logger] =...
method on_event (line 133) | async def on_event(self, event: Event) -> None:
method _handle_task_event (line 144) | async def _handle_task_event(self, event: TaskEvent) -> None:
method _handle_constellation_event (line 157) | async def _handle_constellation_event(self, event: ConstellationEvent)...
method _handle_task_started (line 170) | def _handle_task_started(self, event: TaskEvent) -> None:
method _handle_task_completed (line 179) | def _handle_task_completed(self, event: TaskEvent) -> None:
method _handle_task_failed (line 195) | def _handle_task_failed(self, event: TaskEvent) -> None:
method _handle_constellation_started (line 210) | def _handle_constellation_started(self, event: ConstellationEvent) -> ...
method _handle_constellation_completed (line 233) | def _handle_constellation_completed(self, event: ConstellationEvent) -...
method _handle_constellation_modified (line 262) | def _handle_constellation_modified(self, event: ConstellationEvent) ->...
method get_metrics (line 307) | def get_metrics(self) -> Dict[str, Any]:
method _compute_task_statistics (line 329) | def _compute_task_statistics(self) -> Dict[str, Any]:
method _compute_constellation_statistics (line 367) | def _compute_constellation_statistics(self) -> Dict[str, Any]:
method _compute_modification_statistics (line 417) | def _compute_modification_statistics(self) -> Dict[str, Any]:
FILE: galaxy/session/observers/constellation_sync_observer.py
class ConstellationModificationSynchronizer (line 45) | class ConstellationModificationSynchronizer(IEventObserver):
method __init__ (line 58) | def __init__(
method on_event (line 89) | async def on_event(self, event: Event) -> None:
method _handle_task_event (line 100) | async def _handle_task_event(self, event: TaskEvent) -> None:
method _handle_constellation_event (line 152) | async def _handle_constellation_event(self, event: ConstellationEvent)...
method _auto_complete_on_timeout (line 218) | async def _auto_complete_on_timeout(
method wait_for_pending_modifications (line 247) | async def wait_for_pending_modifications(
method get_current_constellation (line 313) | def get_current_constellation(self) -> Optional[TaskConstellation]:
method has_pending_modifications (line 322) | def has_pending_modifications(self) -> bool:
method get_pending_count (line 330) | def get_pending_count(self) -> int:
method get_pending_task_ids (line 338) | def get_pending_task_ids(self) -> list:
method get_statistics (line 346) | def get_statistics(self) -> Dict[str, int]:
method clear_pending_modifications (line 354) | def clear_pending_modifications(self) -> None:
method set_modification_timeout (line 373) | def set_modification_timeout(self, timeout: float) -> None:
method merge_and_sync_constellation_states (line 384) | def merge_and_sync_constellation_states(
method _is_state_more_advanced (line 453) | def _is_state_more_advanced(self, state1, state2) -> bool:
FILE: galaxy/session/observers/constellation_visualization_handler.py
class ConstellationVisualizationHandler (line 18) | class ConstellationVisualizationHandler:
method __init__ (line 26) | def __init__(
method handle_constellation_started (line 39) | async def handle_constellation_started(
method handle_constellation_completed (line 67) | async def handle_constellation_completed(
method handle_constellation_failed (line 97) | async def handle_constellation_failed(
method handle_constellation_modified (line 127) | async def handle_constellation_modified(
method handle_constellation_event (line 184) | async def handle_constellation_event(
FILE: galaxy/session/observers/dag_visualization_observer.py
class DAGVisualizationObserver (line 19) | class DAGVisualizationObserver(IEventObserver):
method __init__ (line 28) | def __init__(self, enable_visualization: bool = True, console=None):
method _init_visualizer (line 51) | def _init_visualizer(self) -> None:
method on_event (line 72) | async def on_event(self, event: Event) -> None:
method _handle_constellation_event (line 89) | async def _handle_constellation_event(self, event: ConstellationEvent)...
method _handle_task_event (line 110) | async def _handle_task_event(self, event: TaskEvent) -> None:
method _extract_constellation_from_event (line 129) | def _extract_constellation_from_event(
method set_visualization_enabled (line 148) | def set_visualization_enabled(self, enabled: bool) -> None:
method get_constellation (line 158) | def get_constellation(self, constellation_id: str) -> Optional[TaskCon...
method register_constellation (line 167) | def register_constellation(
method clear_constellations (line 178) | def clear_constellations(self) -> None:
FILE: galaxy/session/observers/task_visualization_handler.py
class TaskVisualizationHandler (line 17) | class TaskVisualizationHandler:
method __init__ (line 25) | def __init__(
method handle_task_started (line 38) | async def handle_task_started(
method handle_task_completed (line 70) | async def handle_task_completed(
method handle_task_failed (line 108) | async def handle_task_failed(
method handle_task_event (line 152) | async def handle_task_event(
FILE: galaxy/trajectory/galaxy_parser.py
class GalaxyTrajectory (line 30) | class GalaxyTrajectory:
method __init__ (line 45) | def __init__(self, folder_path: str) -> None:
method _load_response_data (line 63) | def _load_response_data(self) -> List[Dict[str, Any]]:
method _load_evaluation_data (line 84) | def _load_evaluation_data(self) -> Dict[str, Any]:
method step_log (line 101) | def step_log(self) -> List[Dict[str, Any]]:
method evaluation_log (line 106) | def evaluation_log(self) -> Dict[str, Any]:
method request (line 111) | def request(self) -> Optional[str]:
method total_steps (line 118) | def total_steps(self) -> int:
method total_cost (line 123) | def total_cost(self) -> float:
method total_time (line 128) | def total_time(self) -> float:
method _parse_constellation (line 132) | def _parse_constellation(
method _format_task_table (line 215) | def _format_task_table(self, tasks: Dict[str, Any]) -> str:
method _generate_topology_image (line 246) | def _generate_topology_image(
method _format_dependency_graph (line 528) | def _format_dependency_graph(
method _format_dependency_details (line 564) | def _format_dependency_details(self, dependencies: Dict[str, Any]) -> ...
method _format_task_details (line 637) | def _format_task_details(self, tasks: Dict[str, Any]) -> str:
method to_markdown (line 724) | def to_markdown(
FILE: galaxy/trajectory/generate_report.py
function main (line 20) | def main():
FILE: galaxy/visualization/change_detector.py
class VisualizationChangeDetector (line 19) | class VisualizationChangeDetector:
method calculate_constellation_changes (line 28) | def calculate_constellation_changes(
method _determine_modification_type (line 125) | def _determine_modification_type(changes: Dict[str, Any]) -> str:
method _task_properties_changed (line 152) | def _task_properties_changed(old_task, new_task) -> bool:
method _dependency_properties_changed (line 187) | def _dependency_properties_changed(
method format_change_summary (line 217) | def format_change_summary(changes: Dict[str, Any]) -> Dict[str, str]:
FILE: galaxy/visualization/client_display.py
class ClientDisplay (line 23) | class ClientDisplay:
method __init__ (line 31) | def __init__(self, console: Optional[Console] = None):
method show_galaxy_banner (line 39) | def show_galaxy_banner(self) -> None:
method show_welcome_with_usage (line 51) | def show_welcome_with_usage(self) -> None:
method show_interactive_banner (line 72) | def show_interactive_banner(self) -> None:
method show_help (line 86) | def show_help(self) -> None:
method show_status (line 115) | def show_status(
method display_result (line 156) | def display_result(self, result: Dict[str, Any]) -> None:
method show_initialization_progress (line 213) | def show_initialization_progress(self) -> Progress:
method show_processing_request (line 227) | def show_processing_request(self, request_text: str) -> None:
method show_execution_complete (line 240) | def show_execution_complete(self) -> None:
method show_demo_banner (line 248) | def show_demo_banner(self) -> None:
method show_demo_step (line 260) | def show_demo_step(self, step_number: int, request: str) -> None:
method show_demo_complete (line 271) | def show_demo_complete(self) -> None:
method show_processing_status (line 283) | def show_processing_status(self, message: str) -> None:
method print_info (line 291) | def print_info(self, message: str) -> None:
method print_success (line 299) | def print_success(self, message: str) -> None:
method print_error (line 307) | def print_error(self, message: str) -> None:
method print_warning (line 315) | def print_warning(self, message: str) -> None:
method clear_screen (line 323) | def clear_screen(self) -> None:
method get_user_input (line 327) | def get_user_input(self, prompt_text: str) -> str:
FILE: galaxy/visualization/constellation_display.py
class ConstellationDisplay (line 26) | class ConstellationDisplay:
method __init__ (line 34) | def __init__(self, console: Optional[Console] = None):
method display_constellation_started (line 42) | def display_constellation_started(
method display_constellation_completed (line 66) | def display_constellation_completed(
method display_constellation_failed (line 134) | def display_constellation_failed(
method display_constellation_modified (line 165) | def display_constellation_modified(
method _create_basic_info_panel (line 218) | def _create_basic_info_panel(
method _create_basic_stats_panel (line 269) | def _create_basic_stats_panel(self, constellation: "TaskConstellation"...
method _add_change_details_to_table (line 296) | def _add_change_details_to_table(
method _add_constellation_stats_to_table (line 349) | def _add_constellation_stats_to_table(
method _get_constellation_statistics (line 381) | def _get_constellation_statistics(
method _get_ready_task_count (line 422) | def _get_ready_task_count(self, constellation: "TaskConstellation") ->...
method _calculate_success_rate (line 434) | def _calculate_success_rate(self, status_counts: Dict[str, int]) -> Op...
method _calculate_basic_statistics (line 447) | def _calculate_basic_statistics(
method _get_state_text (line 470) | def _get_state_text(self, state: ConstellationState) -> str:
FILE: galaxy/visualization/constellation_formatter.py
class ConstellationFormatter (line 18) | class ConstellationFormatter:
method __init__ (line 21) | def __init__(self):
method format_duration (line 24) | def format_duration(self, seconds: float) -> str:
method format_timestamp (line 38) | def format_timestamp(self, timestamp: str) -> str:
method create_overview_table (line 46) | def create_overview_table(self, data: Dict[str, Any]) -> Table:
method create_statistics_table (line 82) | def create_statistics_table(self, stats: Dict[str, Any]) -> Table:
method create_critical_path_panel (line 131) | def create_critical_path_panel(self, stats: Dict[str, Any]) -> Optiona...
method display_constellation_result (line 150) | def display_constellation_result(self, constellation_data: Dict[str, A...
function format_constellation_result (line 203) | def format_constellation_result(result_data: Dict[str, Any]):
FILE: galaxy/visualization/dag_visualizer.py
class DAGVisualizer (line 31) | class DAGVisualizer:
method __init__ (line 39) | def __init__(self, console: Optional[Console] = None):
method display_constellation_overview (line 67) | def display_constellation_overview(
method display_dag_topology (line 104) | def display_dag_topology(self, constellation: "TaskConstellation") -> ...
method display_task_details (line 165) | def display_task_details(self, constellation: "TaskConstellation") -> ...
method display_dependency_summary (line 229) | def display_dependency_summary(self, constellation: "TaskConstellation...
method display_execution_flow (line 284) | def display_execution_flow(self, constellation: "TaskConstellation") -...
method _format_task_for_tree (line 357) | def _format_task_for_tree(self, task: TaskStar, compact: bool = False)...
method _build_topology_layers (line 377) | def _build_topology_layers(
method _get_status_text (line 427) | def _get_status_text(self, status: TaskStatus) -> str:
method _get_priority_color (line 438) | def _get_priority_color(self, priority) -> str:
method _truncate_name (line 455) | def _truncate_name(self, name: str, max_length: int) -> str:
function display_constellation_creation (line 468) | def display_constellation_creation(
function display_constellation_update (line 483) | def display_constellation_update(
function display_execution_progress (line 505) | def display_execution_progress(
function visualize_dag (line 519) | def visualize_dag(
FILE: galaxy/visualization/task_display.py
class TaskDisplay (line 25) | class TaskDisplay:
method __init__ (line 33) | def __init__(self, console: Optional[Console] = None):
method display_task_started (line 41) | def display_task_started(
method display_task_completed (line 70) | def display_task_completed(
method display_task_failed (line 136) | def display_task_failed(
method _format_task_details (line 198) | def _format_task_details(
method get_task_status_icon (line 233) | def get_task_status_icon(self, status: TaskStatus) -> str:
method format_task_summary (line 250) | def format_task_summary(self, task: TaskStar, include_id: bool = True)...
FILE: galaxy/webui/dependencies.py
class AppState (line 21) | class AppState:
method __init__ (line 34) | def __init__(self) -> None:
method websocket_observer (line 49) | def websocket_observer(self) -> Optional[WebSocketObserver]:
method websocket_observer (line 58) | def websocket_observer(self, observer: WebSocketObserver) -> None:
method galaxy_session (line 68) | def galaxy_session(self) -> Optional["GalaxySession"]:
method galaxy_session (line 77) | def galaxy_session(self, session: "GalaxySession") -> None:
method galaxy_client (line 87) | def galaxy_client(self) -> Optional["GalaxyClient"]:
method galaxy_client (line 96) | def galaxy_client(self, client: "GalaxyClient") -> None:
method request_counter (line 106) | def request_counter(self) -> int:
method increment_request_counter (line 114) | def increment_request_counter(self) -> int:
method reset_request_counter (line 123) | def reset_request_counter(self) -> None:
function get_app_state (line 138) | def get_app_state() -> AppState:
FILE: galaxy/webui/frontend/dist/assets/index-Bthiy-Xd.js
function n (line 1) | function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i...
function r (line 1) | function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}
function Wl (line 1) | function Wl(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.c...
function dS (line 9) | function dS(e){return e===null||typeof e!="object"?null:(e=zp&&e[zp]||e[...
function cs (line 9) | function cs(e,t,n){this.props=e,this.context=t,this.refs=tx,this.updater...
function nx (line 9) | function nx(){}
function Mf (line 9) | function Mf(e,t,n){this.props=e,this.context=t,this.refs=tx,this.updater...
function sx (line 9) | function sx(e,t,n){var r,i={},s=null,o=null;if(t!=null)for(r in t.ref!==...
function fS (line 9) | function fS(e,t){return{$$typeof:zo,type:e.type,key:t,ref:e.ref,props:e....
function Lf (line 9) | function Lf(e){return typeof e=="object"&&e!==null&&e.$$typeof===zo}
function hS (line 9) | function hS(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,fun...
function Du (line 9) | function Du(e,t){return typeof e=="object"&&e!==null&&e.key!=null?hS(""+...
function Da (line 9) | function Da(e,t,n,r,i){var s=typeof e;(s==="undefined"||s==="boolean")&&...
function Jo (line 9) | function Jo(e,t,n){if(e==null)return e;var r=[],i=0;return Da(e,r,"","",...
function pS (line 9) | function pS(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(...
function ox (line 9) | function ox(){throw Error("act(...) is not supported in production build...
function ax (line 17) | function ax(e,t,n){var r,i={},s=null,o=null;n!==void 0&&(s=""+n),t.key!=...
function t (line 25) | function t(z,M){var k=z.length;z.push(M);e:for(;0<k;){var F=k-1>>>1,H=z[...
function n (line 25) | function n(z){return z.length===0?null:z[0]}
function r (line 25) | function r(z){if(z.length===0)return null;var M=z[0],k=z.pop();if(k!==M)...
function i (line 25) | function i(z,M){var k=z.sortIndex-M.sortIndex;return k!==0?k:z.id-M.id}
function v (line 25) | function v(z){for(var M=n(u);M!==null;){if(M.callback===null)r(u);else i...
function b (line 25) | function b(z){if(m=!1,v(z),!y)if(n(l)!==null)y=!0,I(N);else{var M=n(u);M...
function N (line 25) | function N(z,M){y=!1,m&&(m=!1,g(P),P=-1),h=!0;var k=f;try{for(v(M),d=n(l...
function L (line 25) | function L(){return!(e.unstable_now()-C<D)}
function j (line 25) | function j(){if(A!==null){var z=e.unstable_now();C=z;var M=!0;try{M=A(!0...
function I (line 25) | function I(z){A=z,S||(S=!0,O())}
function V (line 25) | function V(z,M){P=w(function(){z(e.unstable_now())},M)}
function U (line 33) | function U(e){for(var t="https://reactjs.org/docs/error-decoder.html?inv...
function si (line 33) | function si(e,t){qi(e,t),qi(e+"Capture",t)}
function qi (line 33) | function qi(e,t){for(oo[e]=t,e=0;e<t.length;e++)dx.add(t[e])}
function CS (line 33) | function CS(e){return qc.call($p,e)?!0:qc.call(Vp,e)?!1:_S.test(e)?$p[e]...
function ES (line 33) | function ES(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){c...
function NS (line 33) | function NS(e,t,n,r){if(t===null||typeof t>"u"||ES(e,t,n,r))return!0;if(...
function mt (line 33) | function mt(e,t,n,r,i,s,o){this.acceptsBooleans=t===2||t===3||t===4,this...
function zf (line 33) | function zf(e){return e[1].toUpperCase()}
function Ff (line 33) | function Ff(e,t,n,r){var i=Je.hasOwnProperty(t)?Je[t]:null;(i!==null?i.t...
function ys (line 33) | function ys(e){return e===null||typeof e!="object"?null:(e=Bp&&e[Bp]||e[...
function Is (line 33) | function Is(e){if(Iu===void 0)try{throw Error()}catch(n){var t=n.stack.t...
function Ru (line 34) | function Ru(e,t){if(!e||Lu)return"";Lu=!0;var n=Error.prepareStackTrace;...
function TS (line 37) | function TS(e){switch(e.tag){case 5:return Is(e.type);case 16:return Is(...
function Zc (line 37) | function Zc(e){if(e==null)return null;if(typeof e=="function")return e.d...
function AS (line 37) | function AS(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:r...
function wr (line 37) | function wr(e){switch(typeof e){case"boolean":case"number":case"string":...
function mx (line 37) | function mx(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="inp...
function PS (line 37) | function PS(e){var t=mx(e)?"checked":"value",n=Object.getOwnPropertyDesc...
function ta (line 37) | function ta(e){e._valueTracker||(e._valueTracker=PS(e))}
function gx (line 37) | function gx(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n...
function el (line 37) | function el(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u...
function Jc (line 37) | function Jc(e,t){var n=t.checked;return Me({},t,{defaultChecked:void 0,d...
function Hp (line 37) | function Hp(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checke...
function yx (line 37) | function yx(e,t){t=t.checked,t!=null&&Ff(e,"checked",t,!1)}
function ed (line 37) | function ed(e,t){yx(e,t);var n=wr(t.value),r=t.type;if(n!=null)r==="numb...
function Up (line 37) | function Up(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defau...
function td (line 37) | function td(e,t,n){(t!=="number"||el(e.ownerDocument)!==e)&&(n==null?e.d...
function Li (line 37) | function Li(e,t,n,r){if(e=e.options,t){t={};for(var i=0;i<n.length;i++)t...
function nd (line 37) | function nd(e,t){if(t.dangerouslySetInnerHTML!=null)throw Error(U(91));r...
function Wp (line 37) | function Wp(e,t){var n=t.value;if(n==null){if(n=t.children,t=t.defaultVa...
function xx (line 37) | function xx(e,t){var n=wr(t.value),r=wr(t.defaultValue);n!=null&&(n=""+n...
function Gp (line 37) | function Gp(e){var t=e.textContent;t===e._wrapperState.initialValue&&t!=...
function vx (line 37) | function vx(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";ca...
function rd (line 37) | function rd(e,t){return e==null||e==="http://www.w3.org/1999/xhtml"?vx(t...
function ao (line 37) | function ao(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeT...
function bx (line 37) | function bx(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typ...
function kx (line 37) | function kx(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=...
function id (line 37) | function id(e,t){if(t){if(MS[e]&&(t.children!=null||t.dangerouslySetInne...
function sd (line 37) | function sd(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";swi...
function Bf (line 37) | function Bf(e){return e=e.target||e.srcElement||window,e.correspondingUs...
function Yp (line 37) | function Yp(e){if(e=Vo(e)){if(typeof ad!="function")throw Error(U(280));...
function Sx (line 37) | function Sx(e){Ri?zi?zi.push(e):zi=[e]:Ri=e}
function _x (line 37) | function _x(){if(Ri){var e=Ri,t=zi;if(zi=Ri=null,Yp(e),t)for(e=0;e<t.len...
function Cx (line 37) | function Cx(e,t){return e(t)}
function Ex (line 37) | function Ex(){}
function Nx (line 37) | function Nx(e,t,n){if(zu)return e(t,n);zu=!0;try{return Cx(e,t,n)}finall...
function lo (line 37) | function lo(e,t){var n=e.stateNode;if(n===null)return null;var r=Ql(n);i...
function DS (line 37) | function DS(e,t,n,r,i,s,o,a,l){var u=Array.prototype.slice.call(argument...
function LS (line 37) | function LS(e,t,n,r,i,s,o,a,l){Hs=!1,tl=null,DS.apply(IS,arguments)}
function RS (line 37) | function RS(e,t,n,r,i,s,o,a,l){if(LS.apply(this,arguments),Hs){if(Hs){va...
function oi (line 37) | function oi(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else...
function Tx (line 37) | function Tx(e){if(e.tag===13){var t=e.memoizedState;if(t===null&&(e=e.al...
function qp (line 37) | function qp(e){if(oi(e)!==e)throw Error(U(188))}
function zS (line 37) | function zS(e){var t=e.alternate;if(!t){if(t=oi(e),t===null)throw Error(...
function Ax (line 37) | function Ax(e){return e=zS(e),e!==null?Px(e):null}
function Px (line 37) | function Px(e){if(e.tag===5||e.tag===6)return e;for(e=e.child;e!==null;)...
function BS (line 37) | function BS(e){if(xn&&typeof xn.onCommitFiberRoot=="function")try{xn.onC...
function WS (line 37) | function WS(e){return e>>>=0,e===0?32:31-(HS(e)/US|0)|0}
function Rs (line 37) | function Rs(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:retur...
function il (line 37) | function il(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,i=e.susp...
function GS (line 37) | function GS(e,t){switch(e){case 1:case 2:case 4:return t+250;case 8:case...
function YS (line 37) | function YS(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirati...
function cd (line 37) | function cd(e){return e=e.pendingLanes&-1073741825,e!==0?e:e&1073741824?...
function Ix (line 37) | function Ix(){var e=ra;return ra<<=1,!(ra&4194240)&&(ra=64),e}
function Fu (line 37) | function Fu(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}
function Fo (line 37) | function Fo(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,...
function qS (line 37) | function qS(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLan...
function Uf (line 37) | function Uf(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var...
function Lx (line 37) | function Lx(e){return e&=-e,1<e?4<e?e&268435455?16:536870912:4:1}
function Xp (line 37) | function Xp(e,t){switch(e){case"focusin":case"focusout":lr=null;break;ca...
function vs (line 37) | function vs(e,t,n,r,i,s){return e===null||e.nativeEvent!==s?(e={blockedO...
function XS (line 37) | function XS(e,t,n,r,i){switch(t){case"focusin":return lr=vs(lr,e,t,n,r,i...
function Vx (line 37) | function Vx(e){var t=$r(e.target);if(t!==null){var n=oi(t);if(n!==null){...
function La (line 37) | function La(e){if(e.blockedOn!==null)return!1;for(var t=e.targetContaine...
function Qp (line 37) | function Qp(e,t,n){La(e)&&n.delete(t)}
function QS (line 37) | function QS(){dd=!1,lr!==null&&La(lr)&&(lr=null),ur!==null&&La(ur)&&(ur=...
function ws (line 37) | function ws(e,t){e.blockedOn===t&&(e.blockedOn=null,dd||(dd=!0,Rt.unstab...
function fo (line 37) | function fo(e){function t(i){return ws(i,e)}if(0<sa.length){ws(sa[0],e);...
function ZS (line 37) | function ZS(e,t,n,r){var i=ge,s=Fi.transition;Fi.transition=null;try{ge=...
function JS (line 37) | function JS(e,t,n,r){var i=ge,s=Fi.transition;Fi.transition=null;try{ge=...
function Gf (line 37) | function Gf(e,t,n,r){if(sl){var i=fd(e,t,n,r);if(i===null)qu(e,t,r,ol,n)...
function fd (line 37) | function fd(e,t,n,r){if(ol=null,e=Bf(r),e=$r(e),e!==null)if(t=oi(e),t===...
function $x (line 37) | function $x(e){switch(e){case"cancel":case"click":case"close":case"conte...
function Bx (line 37) | function Bx(){if(Ra)return Ra;var e,t=Yf,n=t.length,r,i="value"in or?or....
function za (line 37) | function za(e){var t=e.keyCode;return"charCode"in e?(e=e.charCode,e===0&...
function oa (line 37) | function oa(){return!0}
function Zp (line 37) | function Zp(){return!1}
function Vt (line 37) | function Vt(e){function t(n,r,i,s,o){this._reactName=n,this._targetInst=...
function f_ (line 37) | function f_(e){var t=this.nativeEvent;return t.getModifierState?t.getMod...
function Kf (line 37) | function Kf(){return f_}
function Ux (line 37) | function Ux(e,t){switch(e){case"keyup":return k_.indexOf(t.keyCode)!==-1...
function Wx (line 37) | function Wx(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:n...
function __ (line 37) | function __(e,t){switch(e){case"compositionend":return Wx(t);case"keypre...
function C_ (line 37) | function C_(e,t){if(wi)return e==="compositionend"||!Xf&&Ux(e,t)?(e=Bx()...
function im (line 37) | function im(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t===...
function Gx (line 37) | function Gx(e,t,n,r){Sx(r),t=al(t,"onChange"),0<t.length&&(n=new qf("onC...
function N_ (line 37) | function N_(e){r1(e,0)}
function Kl (line 37) | function Kl(e){var t=Si(e);if(gx(t))return e}
function T_ (line 37) | function T_(e,t){if(e==="change")return t}
function om (line 37) | function om(){Ws&&(Ws.detachEvent("onpropertychange",qx),ho=Ws=null)}
function qx (line 37) | function qx(e){if(e.propertyName==="value"&&Kl(ho)){var t=[];Gx(t,ho,e,B...
function A_ (line 37) | function A_(e,t,n){e==="focusin"?(om(),Ws=t,ho=n,Ws.attachEvent("onprope...
function P_ (line 37) | function P_(e){if(e==="selectionchange"||e==="keyup"||e==="keydown")retu...
function j_ (line 37) | function j_(e,t){if(e==="click")return Kl(t)}
function M_ (line 37) | function M_(e,t){if(e==="input"||e==="change")return Kl(t)}
function D_ (line 37) | function D_(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}
function po (line 37) | function po(e,t){if(un(e,t))return!0;if(typeof e!="object"||e===null||ty...
function am (line 37) | function am(e){for(;e&&e.firstChild;)e=e.firstChild;return e}
function lm (line 37) | function lm(e,t){var n=am(e);e=0;for(var r;n;){if(n.nodeType===3){if(r=e...
function Kx (line 37) | function Kx(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType...
function Xx (line 37) | function Xx(){for(var e=window,t=el();t instanceof e.HTMLIFrameElement;)...
function Qf (line 37) | function Qf(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(...
function I_ (line 37) | function I_(e){var t=Xx(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n...
function um (line 37) | function um(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.owne...
function aa (line 37) | function aa(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["W...
function Xl (line 37) | function Xl(e){if(Uu[e])return Uu[e];if(!ki[e])return e;var t=ki[e],n;fo...
function _r (line 37) | function _r(e,t){n1.set(e,t),si(t,[e])}
function dm (line 37) | function dm(e,t,n){var r=e.type||"unknown-event";e.currentTarget=n,RS(r,...
function r1 (line 37) | function r1(e,t){t=(t&4)!==0;for(var n=0;n<e.length;n++){var r=e[n],i=r....
function Se (line 37) | function Se(e,t){var n=t[vd];n===void 0&&(n=t[vd]=new Set);var r=e+"__bu...
function Yu (line 37) | function Yu(e,t,n){var r=0;t&&(r|=4),i1(n,e,r,t)}
function mo (line 37) | function mo(e){if(!e[la]){e[la]=!0,dx.forEach(function(n){n!=="selection...
function i1 (line 37) | function i1(e,t,n,r){switch($x(t)){case 1:var i=ZS;break;case 4:i=JS;bre...
function qu (line 37) | function qu(e,t,n,r,i){var s=r;if(!(t&1)&&!(t&2)&&r!==null)e:for(;;){if(...
function go (line 37) | function go(e,t,n){return{instance:e,listener:t,currentTarget:n}}
function al (line 37) | function al(e,t){for(var n=t+"Capture",r=[];e!==null;){var i=e,s=i.state...
function di (line 37) | function di(e){if(e===null)return null;do e=e.return;while(e&&e.tag!==5)...
function fm (line 37) | function fm(e,t,n,r,i){for(var s=t._reactName,o=[];n!==null&&n!==r;){var...
function hm (line 37) | function hm(e){return(typeof e=="string"?e:""+e).replace(O_,`
function ua (line 38) | function ua(e,t,n){if(t=hm(t),hm(e)!==t&&n)throw Error(U(425))}
function ll (line 38) | function ll(){}
function yd (line 38) | function yd(e,t){return e==="textarea"||e==="noscript"||typeof t.childre...
function H_ (line 38) | function H_(e){setTimeout(function(){throw e})}
function Ku (line 38) | function Ku(e,t){var n=t,r=0;do{var i=n.nextSibling;if(e.removeChild(n),...
function dr (line 38) | function dr(e){for(;e!=null;e=e.nextSibling){var t=e.nodeType;if(t===1||...
function mm (line 38) | function mm(e){e=e.previousSibling;for(var t=0;e;){if(e.nodeType===8){va...
function $r (line 38) | function $r(e){var t=e[yn];if(t)return t;for(var n=e.parentNode;n;){if(t...
function Vo (line 38) | function Vo(e){return e=e[yn]||e[zn],!e||e.tag!==5&&e.tag!==6&&e.tag!==1...
function Si (line 38) | function Si(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(U(...
function Ql (line 38) | function Ql(e){return e[yo]||null}
function Cr (line 38) | function Cr(e){return{current:e}}
function _e (line 38) | function _e(e){0>_i||(e.current=wd[_i],wd[_i]=null,_i--)}
function be (line 38) | function be(e,t){_i++,wd[_i]=e.current,e.current=t}
function Ki (line 38) | function Ki(e,t){var n=e.type.contextTypes;if(!n)return br;var r=e.state...
function St (line 38) | function St(e){return e=e.childContextTypes,e!=null}
function ul (line 38) | function ul(){_e(kt),_e(ot)}
function gm (line 38) | function gm(e,t,n){if(ot.current!==br)throw Error(U(168));be(ot,t),be(kt...
function s1 (line 38) | function s1(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.g...
function cl (line 38) | function cl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMerged...
function ym (line 38) | function ym(e,t,n){var r=e.stateNode;if(!r)throw Error(U(169));n?(e=s1(e...
function o1 (line 38) | function o1(e){Nn===null?Nn=[e]:Nn.push(e)}
function G_ (line 38) | function G_(e){Zl=!0,o1(e)}
function Er (line 38) | function Er(){if(!Xu&&Nn!==null){Xu=!0;var e=0,t=ge;try{var n=Nn;for(ge=...
function Ir (line 38) | function Ir(e,t){Ci[Ei++]=fl,Ci[Ei++]=dl,dl=e,fl=t}
function a1 (line 38) | function a1(e,t,n){Ht[Ut++]=Tn,Ht[Ut++]=An,Ht[Ut++]=Zr,Zr=e;var r=Tn;e=A...
function Zf (line 38) | function Zf(e){e.return!==null&&(Ir(e,1),a1(e,1,0))}
function Jf (line 38) | function Jf(e){for(;e===dl;)dl=Ci[--Ei],Ci[Ei]=null,fl=Ci[--Ei],Ci[Ei]=n...
function l1 (line 38) | function l1(e,t){var n=Gt(5,null,null,0);n.elementType="DELETED",n.state...
function xm (line 38) | function xm(e,t){switch(e.tag){case 5:var n=e.type;return t=t.nodeType!=...
function bd (line 38) | function bd(e){return(e.mode&1)!==0&&(e.flags&128)===0}
function kd (line 38) | function kd(e){if(Ne){var t=Dt;if(t){var n=t;if(!xm(e,t)){if(bd(e))throw...
function vm (line 38) | function vm(e){for(e=e.return;e!==null&&e.tag!==5&&e.tag!==3&&e.tag!==13...
function ca (line 38) | function ca(e){if(e!==It)return!1;if(!Ne)return vm(e),Ne=!0,!1;var t;if(...
function u1 (line 38) | function u1(){for(var e=Dt;e;)e=dr(e.nextSibling)}
function Xi (line 38) | function Xi(){Dt=It=null,Ne=!1}
function eh (line 38) | function eh(e){tn===null?tn=[e]:tn.push(e)}
function ks (line 38) | function ks(e,t,n){if(e=n.ref,e!==null&&typeof e!="function"&&typeof e!=...
function da (line 38) | function da(e,t){throw e=Object.prototype.toString.call(t),Error(U(31,e=...
function wm (line 38) | function wm(e){var t=e._init;return t(e._payload)}
function c1 (line 38) | function c1(e){function t(g,x){if(e){var v=g.deletions;v===null?(g.delet...
function nh (line 38) | function nh(){th=Ni=pl=null}
function rh (line 38) | function rh(e){var t=hl.current;_e(hl),e._currentValue=t}
function Sd (line 38) | function Sd(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)...
function Oi (line 38) | function Oi(e,t){pl=e,th=Ni=null,e=e.dependencies,e!==null&&e.firstConte...
function Kt (line 38) | function Kt(e){var t=e._currentValue;if(th!==e)if(e={context:e,memoizedV...
function ih (line 38) | function ih(e){Br===null?Br=[e]:Br.push(e)}
function f1 (line 38) | function f1(e,t,n,r){var i=t.interleaved;return i===null?(n.next=n,ih(t)...
function Fn (line 38) | function Fn(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t)...
function sh (line 38) | function sh(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:...
function h1 (line 38) | function h1(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={base...
function Dn (line 38) | function Dn(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:n...
function fr (line 38) | function fr(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.sh...
function Fa (line 38) | function Fa(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!...
function bm (line 38) | function bm(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.upd...
function ml (line 38) | function ml(e,t,n,r){var i=e.updateQueue;Jn=!1;var s=i.firstBaseUpdate,o...
function km (line 38) | function km(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;t<e.le...
function Hr (line 38) | function Hr(e){if(e===$o)throw Error(U(174));return e}
function oh (line 38) | function oh(e,t){switch(be(vo,t),be(xo,e),be(vn,$o),e=t.nodeType,e){case...
function Zi (line 38) | function Zi(){_e(vn),_e(xo),_e(vo)}
function p1 (line 38) | function p1(e){Hr(vo.current);var t=Hr(vn.current),n=rd(t,e.type);t!==n&...
function ah (line 38) | function ah(e){xo.current===e&&(_e(vn),_e(xo))}
function gl (line 38) | function gl(e){for(var t=e;t!==null;){if(t.tag===13){var n=t.memoizedSta...
function lh (line 38) | function lh(){for(var e=0;e<Qu.length;e++)Qu[e]._workInProgressVersionPr...
function tt (line 38) | function tt(){throw Error(U(321))}
function uh (line 38) | function uh(e,t){if(t===null)return!1;for(var n=0;n<t.length&&n<e.length...
function ch (line 38) | function ch(e,t,n,r,i,s){if(Jr=s,je=t,t.memoizedState=null,t.updateQueue...
function dh (line 38) | function dh(){var e=wo!==0;return wo=0,e}
function pn (line 38) | function pn(){var e={memoizedState:null,baseState:null,baseQueue:null,qu...
function Xt (line 38) | function Xt(){if(Ue===null){var e=je.alternate;e=e!==null?e.memoizedStat...
function bo (line 38) | function bo(e,t){return typeof t=="function"?t(e):t}
function Ju (line 38) | function Ju(e){var t=Xt(),n=t.queue;if(n===null)throw Error(U(311));n.la...
function ec (line 38) | function ec(e){var t=Xt(),n=t.queue;if(n===null)throw Error(U(311));n.la...
function m1 (line 38) | function m1(){}
function g1 (line 38) | function g1(e,t){var n=je,r=Xt(),i=t(),s=!un(r.memoizedState,i);if(s&&(r...
function y1 (line 38) | function y1(e,t,n){e.flags|=16384,e={getSnapshot:t,value:n},t=je.updateQ...
function x1 (line 38) | function x1(e,t,n,r){t.value=n,t.getSnapshot=r,w1(t)&&b1(e)}
function v1 (line 38) | function v1(e,t,n){return n(function(){w1(t)&&b1(e)})}
function w1 (line 38) | function w1(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!un(e,n...
function b1 (line 38) | function b1(e){var t=Fn(e,1);t!==null&&on(t,e,1,-1)}
function Sm (line 38) | function Sm(e){var t=pn();return typeof e=="function"&&(e=e()),t.memoize...
function ko (line 38) | function ko(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null...
function k1 (line 38) | function k1(){return Xt().memoizedState}
function Va (line 38) | function Va(e,t,n,r){var i=pn();je.flags|=e,i.memoizedState=ko(1|t,n,voi...
function Jl (line 38) | function Jl(e,t,n,r){var i=Xt();r=r===void 0?null:r;var s=void 0;if(Ue!=...
function _m (line 38) | function _m(e,t){return Va(8390656,8,e,t)}
function fh (line 38) | function fh(e,t){return Jl(2048,8,e,t)}
function S1 (line 38) | function S1(e,t){return Jl(4,2,e,t)}
function _1 (line 38) | function _1(e,t){return Jl(4,4,e,t)}
function C1 (line 38) | function C1(e,t){if(typeof t=="function")return e=e(),t(e),function(){t(...
function E1 (line 38) | function E1(e,t,n){return n=n!=null?n.concat([e]):null,Jl(4,4,C1.bind(nu...
function hh (line 38) | function hh(){}
function N1 (line 38) | function N1(e,t){var n=Xt();t=t===void 0?null:t;var r=n.memoizedState;re...
function T1 (line 38) | function T1(e,t){var n=Xt();t=t===void 0?null:t;var r=n.memoizedState;re...
function A1 (line 38) | function A1(e,t,n){return Jr&21?(un(n,t)||(n=Ix(),je.lanes|=n,ei|=n,e.ba...
function K_ (line 38) | function K_(e,t){var n=ge;ge=n!==0&&4>n?n:4,e(!0);var r=Zu.transition;Zu...
function P1 (line 38) | function P1(){return Xt().memoizedState}
function X_ (line 38) | function X_(e,t,n){var r=pr(e);if(n={lane:r,action:n,hasEagerState:!1,ea...
function Q_ (line 38) | function Q_(e,t,n){var r=pr(e),i={lane:r,action:n,hasEagerState:!1,eager...
function j1 (line 38) | function j1(e){var t=e.alternate;return e===je||t!==null&&t===je}
function M1 (line 38) | function M1(e,t){Ys=yl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.ne...
function D1 (line 38) | function D1(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t....
function Jt (line 38) | function Jt(e,t){if(e&&e.defaultProps){t=Me({},t),e=e.defaultProps;for(v...
function _d (line 38) | function _d(e,t,n,r){t=e.memoizedState,n=n(r,t),n=n==null?t:Me({},t,n),e...
function Cm (line 38) | function Cm(e,t,n,r,i,s,o){return e=e.stateNode,typeof e.shouldComponent...
function I1 (line 38) | function I1(e,t,n){var r=!1,i=br,s=t.contextType;return typeof s=="objec...
function Em (line 38) | function Em(e,t,n,r){e=t.state,typeof t.componentWillReceiveProps=="func...
function Cd (line 38) | function Cd(e,t,n,r){var i=e.stateNode;i.props=n,i.state=e.memoizedState...
function Ji (line 38) | function Ji(e,t){try{var n="",r=t;do n+=TS(r),r=r.return;while(r);var i=...
function tc (line 40) | function tc(e,t,n){return{value:e,source:null,stack:n??null,digest:t??nu...
function Ed (line 40) | function Ed(e,t){try{console.error(t.value)}catch(n){setTimeout(function...
function L1 (line 40) | function L1(e,t,n){n=Dn(-1,n),n.tag=3,n.payload={element:null};var r=t.v...
function R1 (line 40) | function R1(e,t,n){n=Dn(-1,n),n.tag=3;var r=e.type.getDerivedStateFromEr...
function Nm (line 40) | function Nm(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new tC;v...
function Tm (line 40) | function Tm(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null...
function Am (line 40) | function Am(e,t,n,r,i){return e.mode&1?(e.flags|=65536,e.lanes=i,e):(e==...
function dt (line 40) | function dt(e,t,n,r){t.child=e===null?d1(t,null,n,r):Qi(t,e.child,n,r)}
function Pm (line 40) | function Pm(e,t,n,r,i){n=n.render;var s=t.ref;return Oi(t,i),r=ch(e,t,n,...
function jm (line 40) | function jm(e,t,n,r,i){if(e===null){var s=n.type;return typeof s=="funct...
function z1 (line 40) | function z1(e,t,n,r,i){if(e!==null){var s=e.memoizedProps;if(po(s,r)&&e....
function F1 (line 40) | function F1(e,t,n){var r=t.pendingProps,i=r.children,s=e!==null?e.memoiz...
function O1 (line 40) | function O1(e,t){var n=t.ref;(e===null&&n!==null||e!==null&&e.ref!==n)&&...
function Nd (line 40) | function Nd(e,t,n,r,i){var s=St(n)?Qr:ot.current;return s=Ki(t,s),Oi(t,i...
function Mm (line 40) | function Mm(e,t,n,r,i){if(St(n)){var s=!0;cl(t)}else s=!1;if(Oi(t,i),t.s...
function Td (line 40) | function Td(e,t,n,r,i,s){O1(e,t);var o=(t.flags&128)!==0;if(!r&&!o)retur...
function V1 (line 40) | function V1(e){var t=e.stateNode;t.pendingContext?gm(e,t.pendingContext,...
function Dm (line 40) | function Dm(e,t,n,r,i){return Xi(),eh(i),t.flags|=256,dt(e,t,n,r),t.child}
function Pd (line 40) | function Pd(e){return{baseLanes:e,cachePool:null,transitions:null}}
function $1 (line 40) | function $1(e,t,n){var r=t.pendingProps,i=Ae.current,s=!1,o=(t.flags&128...
function ph (line 40) | function ph(e,t){return t=ru({mode:"visible",children:t},e.mode,0,null),...
function fa (line 40) | function fa(e,t,n,r){return r!==null&&eh(r),Qi(t,e.child,null,n),e=ph(t,...
function rC (line 40) | function rC(e,t,n,r,i,s,o){if(n)return t.flags&256?(t.flags&=-257,r=tc(E...
function Im (line 40) | function Im(e,t,n){e.lanes|=t;var r=e.alternate;r!==null&&(r.lanes|=t),S...
function nc (line 40) | function nc(e,t,n,r,i){var s=e.memoizedState;s===null?e.memoizedState={i...
function B1 (line 40) | function B1(e,t,n){var r=t.pendingProps,i=r.revealOrder,s=r.tail;if(dt(e...
function $a (line 40) | function $a(e,t){!(t.mode&1)&&e!==null&&(e.alternate=null,t.alternate=nu...
function On (line 40) | function On(e,t,n){if(e!==null&&(t.dependencies=e.dependencies),ei|=t.la...
function iC (line 40) | function iC(e,t,n){switch(t.tag){case 3:V1(t),Xi();break;case 5:p1(t);br...
function Ss (line 40) | function Ss(e,t){if(!Ne)switch(e.tailMode){case"hidden":t=e.tail;for(var...
function nt (line 40) | function nt(e){var t=e.alternate!==null&&e.alternate.child===e.child,n=0...
function sC (line 40) | function sC(e,t,n){var r=t.pendingProps;switch(Jf(t),t.tag){case 2:case ...
function oC (line 40) | function oC(e,t){switch(Jf(t),t.tag){case 1:return St(t.type)&&ul(),e=t....
function Ti (line 40) | function Ti(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(n...
function Md (line 40) | function Md(e,t,n){try{n()}catch(r){Le(e,t,r)}}
function lC (line 40) | function lC(e,t){if(md=sl,e=Xx(),Qf(e)){if("selectionStart"in e)var n={s...
function qs (line 40) | function qs(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r...
function tu (line 40) | function tu(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==nul...
function Dd (line 40) | function Dd(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){...
function G1 (line 40) | function G1(e){var t=e.alternate;t!==null&&(e.alternate=null,G1(t)),e.ch...
function Y1 (line 40) | function Y1(e){return e.tag===5||e.tag===3||e.tag===4}
function Rm (line 40) | function Rm(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Y1(...
function Id (line 40) | function Id(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeTyp...
function Ld (line 40) | function Ld(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertB...
function qn (line 40) | function qn(e,t,n){for(n=n.child;n!==null;)q1(e,t,n),n=n.sibling}
function q1 (line 40) | function q1(e,t,n){if(xn&&typeof xn.onCommitFiberUnmount=="function")try...
function zm (line 40) | function zm(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n...
function Zt (line 40) | function Zt(e,t){var n=t.deletions;if(n!==null)for(var r=0;r<n.length;r+...
function K1 (line 40) | function K1(e,t){var n=e.alternate,r=e.flags;switch(e.tag){case 0:case 1...
function fn (line 40) | function fn(e){var t=e.flags;if(t&2){try{e:{for(var n=e.return;n!==null;...
function uC (line 40) | function uC(e,t,n){q=e,X1(e)}
function X1 (line 40) | function X1(e,t,n){for(var r=(e.mode&1)!==0;q!==null;){var i=q,s=i.child...
function Fm (line 40) | function Fm(e){for(;q!==null;){var t=q;if(t.flags&8772){var n=t.alternat...
function Om (line 40) | function Om(e){for(;q!==null;){var t=q;if(t===e){q=null;break}var n=t.si...
function Vm (line 40) | function Vm(e){for(;q!==null;){var t=q;try{switch(t.tag){case 0:case 11:...
function ht (line 40) | function ht(){return me&6?ze():Ba!==-1?Ba:Ba=ze()}
function pr (line 40) | function pr(e){return e.mode&1?me&2&&Ze!==0?Ze&-Ze:Y_.transition!==null?...
function on (line 40) | function on(e,t,n,r){if(50<Xs)throw Xs=0,zd=null,Error(U(185));Fo(e,n,r)...
function _t (line 40) | function _t(e,t){var n=e.callbackNode;YS(e,t);var r=il(e,e===Ye?Ze:0);if...
function Q1 (line 40) | function Q1(e,t){if(Ba=-1,Ha=0,me&6)throw Error(U(327));var n=e.callback...
function Fd (line 40) | function Fd(e,t){var n=Ks;return e.current.memoizedState.isDehydrated&&(...
function Od (line 40) | function Od(e){vt===null?vt=e:vt.push.apply(vt,e)}
function dC (line 40) | function dC(e){for(var t=e;;){if(t.flags&16384){var n=t.updateQueue;if(n...
function ir (line 40) | function ir(e,t){for(t&=~gh,t&=~nu,e.suspendedLanes|=t,e.pingedLanes&=~t...
function $m (line 40) | function $m(e){if(me&6)throw Error(U(327));Vi();var t=il(e,0);if(!(t&1))...
function xh (line 40) | function xh(e,t){var n=me;me|=1;try{return e(t)}finally{me=n,me===0&&(es...
function ti (line 40) | function ti(e){ar!==null&&ar.tag===0&&!(me&6)&&Vi();var t=me;me|=1;var n...
function vh (line 40) | function vh(){Mt=Ai.current,_e(Ai)}
function Yr (line 40) | function Yr(e,t){e.finishedWork=null,e.finishedLanes=0;var n=e.timeoutHa...
function Z1 (line 40) | function Z1(e,t){do{var n=Be;try{if(nh(),Oa.current=xl,yl){for(var r=je....
function J1 (line 40) | function J1(){var e=vl.current;return vl.current=xl,e===null?xl:e}
function wh (line 40) | function wh(){(We===0||We===3||We===2)&&(We=4),Ye===null||!(ei&268435455...
function kl (line 40) | function kl(e,t){var n=me;me|=2;var r=J1();(Ye!==e||Ze!==t)&&(En=null,Yr...
function fC (line 40) | function fC(){for(;Be!==null;)ev(Be)}
function hC (line 40) | function hC(){for(;Be!==null&&!FS();)ev(Be)}
function ev (line 40) | function ev(e){var t=rv(e.alternate,e,Mt);e.memoizedProps=e.pendingProps...
function tv (line 40) | function tv(e){var t=e;do{var n=t.alternate;if(e=t.return,t.flags&32768)...
function Lr (line 40) | function Lr(e,t,n){var r=ge,i=qt.transition;try{qt.transition=null,ge=1,...
function pC (line 40) | function pC(e,t,n,r){do Vi();while(ar!==null);if(me&6)throw Error(U(327)...
function Vi (line 40) | function Vi(){if(ar!==null){var e=Lx(bl),t=qt.transition,n=ge;try{if(qt....
function Bm (line 40) | function Bm(e,t,n){t=Ji(n,t),t=L1(e,t,1),e=fr(e,t,1),t=ht(),e!==null&&(F...
function Le (line 40) | function Le(e,t,n){if(e.tag===3)Bm(e,e,n);else for(;t!==null;){if(t.tag=...
function mC (line 40) | function mC(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),t=ht(),e.ping...
function nv (line 40) | function nv(e,t){t===0&&(e.mode&1?(t=ia,ia<<=1,!(ia&130023424)&&(ia=4194...
function gC (line 40) | function gC(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),nv(e,n)}
function yC (line 40) | function yC(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,i=e.mem...
function iv (line 40) | function iv(e,t){return jx(e,t)}
function xC (line 40) | function xC(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this....
function Gt (line 40) | function Gt(e,t,n,r){return new xC(e,t,n,r)}
function bh (line 40) | function bh(e){return e=e.prototype,!(!e||!e.isReactComponent)}
function vC (line 40) | function vC(e){if(typeof e=="function")return bh(e)?1:0;if(e!=null){if(e...
function mr (line 40) | function mr(e,t){var n=e.alternate;return n===null?(n=Gt(e.tag,t,e.key,e...
function Ua (line 40) | function Ua(e,t,n,r,i,s){var o=2;if(r=e,typeof e=="function")bh(e)&&(o=1...
function qr (line 40) | function qr(e,t,n,r){return e=Gt(7,e,r,t),e.lanes=n,e}
function ru (line 40) | function ru(e,t,n,r){return e=Gt(22,e,r,t),e.elementType=px,e.lanes=n,e....
function rc (line 40) | function rc(e,t,n){return e=Gt(6,e,null,t),e.lanes=n,e}
function ic (line 40) | function ic(e,t,n){return t=Gt(4,e.children!==null?e.children:[],e.key,t...
function wC (line 40) | function wC(e,t,n,r,i){this.tag=t,this.containerInfo=e,this.finishedWork...
function kh (line 40) | function kh(e,t,n,r,i,s,o,a,l){return e=new wC(e,t,n,a,l),t===1?(t=1,s==...
function bC (line 40) | function bC(e,t,n){var r=3<arguments.length&&arguments[3]!==void 0?argum...
function sv (line 40) | function sv(e){if(!e)return br;e=e._reactInternals;e:{if(oi(e)!==e||e.ta...
function ov (line 40) | function ov(e,t,n,r,i,s,o,a,l){return e=kh(n,r,!0,e,i,s,o,a,l),e.context...
function iu (line 40) | function iu(e,t,n,r){var i=t.current,s=ht(),o=pr(i);return n=sv(n),t.con...
function Sl (line 40) | function Sl(e){if(e=e.current,!e.child)return null;switch(e.child.tag){c...
function Hm (line 40) | function Hm(e,t){if(e=e.memoizedState,e!==null&&e.dehydrated!==null){var...
function Sh (line 40) | function Sh(e,t){Hm(e,t),(e=e.alternate)&&Hm(e,t)}
function kC (line 40) | function kC(){return null}
function _h (line 40) | function _h(e){this._internalRoot=e}
function su (line 40) | function su(e){this._internalRoot=e}
function Ch (line 40) | function Ch(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==...
function ou (line 40) | function ou(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==...
function Um (line 40) | function Um(){}
function SC (line 40) | function SC(e,t,n,r,i){if(i){if(typeof r=="function"){var s=r;r=function...
function au (line 40) | function au(e,t,n,r,i){var s=n._reactRootContainer;if(s){var o=s;if(type...
function lv (line 40) | function lv(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __R...
function Oe (line 40) | function Oe(e,t){if(Object.is(e,t))return!0;if(typeof e!="object"||e===n...
function mv (line 265) | function mv(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+...
function de (line 265) | function de(){for(var e,t,n=0,r="",i=arguments.length;n<i;n++)(e=argumen...
function JC (line 273) | function JC(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}
function s5 (line 273) | function s5(e,t){var n=t(),r=t5({inst:{value:n,getSnapshot:t}}),i=r[0].i...
function oc (line 273) | function oc(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!e5(e,n...
function o5 (line 273) | function o5(e,t){return t()}
function c5 (line 281) | function c5(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}
function l (line 281) | function l(h){if(!u){if(u=!0,c=h,h=r(h),i!==void 0&&o.hasValue){var y=o....
function b5 (line 281) | function b5(e,t=w5,n){(kv?"production":void 0)!=="production"&&n&&!Xm&&(...
class _5 (line 281) | class _5{constructor(t){_n(this,"ws",null);_n(this,"url");_n(this,"recon...
method constructor (line 281) | constructor(t){_n(this,"ws",null);_n(this,"url");_n(this,"reconnectAtt...
method connect (line 281) | connect(){return new Promise((t,n)=>{try{this.notifyStatus("connecting...
method attemptReconnect (line 281) | attemptReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempt...
method disconnect (line 281) | disconnect(){this.isIntentionalClose=!0,this.ws&&(this.ws.close(),this...
method send (line 281) | send(t){this.ws&&this.ws.readyState===WebSocket.OPEN?this.ws.send(JSON...
method sendRequest (line 281) | sendRequest(t){this.send({type:"request",text:t,timestamp:Date.now()})}
method sendReset (line 281) | sendReset(){this.send({type:"reset",timestamp:Date.now()})}
method sendPing (line 281) | sendPing(){this.send({type:"ping",timestamp:Date.now()})}
method onEvent (line 281) | onEvent(t){return this.eventCallbacks.add(t),()=>{this.eventCallbacks....
method onStatusChange (line 281) | onStatusChange(t){return this.statusCallbacks.add(t),()=>{this.statusC...
method notifyCallbacks (line 281) | notifyCallbacks(t){console.log("🎯 notifyCallbacks called with event:",...
method notifyStatus (line 281) | notifyStatus(t){this.statusCallbacks.forEach(n=>{try{n(t)}catch(r){con...
method isConnected (line 281) | get isConnected(){return this.ws!==null&&this.ws.readyState===WebSocke...
function Pn (line 281) | function Pn(){return ac||(ac=new _5),ac}
function A5 (line 281) | function A5(e,t){const n={};return(e[e.length-1]===""?[...e,""]:e).join(...
function eg (line 281) | function eg(e,t){return(M5.jsx?j5:P5).test(e)}
function I5 (line 281) | function I5(e){return typeof e=="object"?e.type==="text"?tg(e.value):!1:...
function tg (line 281) | function tg(e){return e.replace(D5,"")===""}
class Ho (line 281) | class Ho{constructor(t,n,r){this.normal=n,this.property=t,r&&(this.space...
method constructor (line 281) | constructor(t,n,r){this.normal=n,this.property=t,r&&(this.space=r)}
function Sv (line 281) | function Sv(e,t){const n={},r={};for(const i of e)Object.assign(n,i.prop...
function Hd (line 281) | function Hd(e){return e.toLowerCase()}
class Et (line 281) | class Et{constructor(t,n){this.attribute=n,this.property=t}}
method constructor (line 281) | constructor(t,n){this.attribute=n,this.property=t}
function li (line 281) | function li(){return 2**++L5}
class Eh (line 281) | class Eh extends Et{constructor(t,n,r,i){let s=-1;if(super(t,n),ng(this,...
method constructor (line 281) | constructor(t,n,r,i){let s=-1;if(super(t,n),ng(this,"space",i),typeof ...
function ng (line 281) | function ng(e,t,n){n&&(e[t]=n)}
function ps (line 281) | function ps(e){const t={},n={};for(const[r,i]of Object.entries(e.propert...
method transform (line 281) | transform(e,t){return t==="role"?t:"aria-"+t.slice(4).toLowerCase()}
function Cv (line 281) | function Cv(e,t){return t in e?e[t]:t}
function Ev (line 281) | function Ev(e,t){return Cv(e,t.toLowerCase())}
method transform (line 281) | transform(e,t){return"xlink:"+t.slice(5).toLowerCase()}
method transform (line 281) | transform(e,t){return"xml:"+t.slice(3).toLowerCase()}
function $5 (line 281) | function $5(e,t){const n=Hd(t);let r=t,i=Et;if(n in e.normal)return e.pr...
function B5 (line 281) | function B5(e){return"-"+e.toLowerCase()}
function H5 (line 281) | function H5(e){return e.charAt(1).toUpperCase()}
function W5 (line 281) | function W5(e){return e.join(" ").trim()}
function nE (line 282) | function nE(e,t){if(typeof e!="string")throw new TypeError("First argume...
function ag (line 282) | function ag(e){return e?e.replace(Z5,Vr):Vr}
function oE (line 282) | function oE(e,t){let n=null;if(!e||typeof e!="string")return n;const r=(...
function Gd (line 282) | function Gd(e,t){var n={};return!e||typeof e!="string"||(0,gE.default)(e...
function jv (line 282) | function jv(e){return t;function t(n){const r=n&&n.position&&n.position[...
function wE (line 282) | function wE(e){const t=Ah(e),n=Pv(e);if(t&&n)return{start:t,end:n}}
function Zs (line 282) | function Zs(e){return!e||typeof e!="object"?"":"position"in e||"type"in ...
function Yd (line 282) | function Yd(e){return cg(e&&e.line)+":"+cg(e&&e.column)}
function ug (line 282) | function ug(e){return Yd(e&&e.start)+"-"+Yd(e&&e.end)}
function cg (line 282) | function cg(e){return e&&typeof e=="number"?e:1}
class at (line 282) | class at extends Error{constructor(t,n,r){super(),typeof n=="string"&&(r...
method constructor (line 282) | constructor(t,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i=""...
function CE (line 282) | function CE(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expecte...
function Dv (line 282) | function Dv(e,t,n){if(t.type==="element")return EE(e,t,n);if(t.type==="m...
function EE (line 282) | function EE(e,t,n){const r=e.schema;let i=r;t.tagName.toLowerCase()==="s...
function NE (line 282) | function NE(e,t){if(t.data&&t.data.estree&&e.evaluater){const r=t.data.e...
function TE (line 282) | function TE(e,t){if(t.data&&t.data.estree&&e.evaluater)return e.evaluate...
function AE (line 282) | function AE(e,t,n){const r=e.schema;let i=r;t.name==="svg"&&r.space==="h...
function PE (line 282) | function PE(e,t,n){const r={};return jh(r,Mh(e,t)),e.create(t,e.Fragment...
function jE (line 282) | function jE(e,t){return t.value}
function Iv (line 282) | function Iv(e,t,n,r){typeof n!="string"&&n!==e.Fragment&&e.passNode&&(t....
function jh (line 282) | function jh(e,t){if(t.length>0){const n=t.length>1?t:t[0];n&&(e.children...
function ME (line 282) | function ME(e,t,n){return r;function r(i,s,o,a){const u=Array.isArray(o....
function DE (line 282) | function DE(e,t){return n;function n(r,i,s,o){const a=Array.isArray(s.ch...
function IE (line 282) | function IE(e,t){const n={};let r,i;for(i in t.properties)if(i!=="childr...
function LE (line 282) | function LE(e,t){const n={};for(const r of t.attributes)if(r.type==="mdx...
function Mh (line 282) | function Mh(e,t){const n=[];let r=-1;const i=e.passKeys?new Map:bE;for(;...
function RE (line 282) | function RE(e,t,n){const r=$5(e.schema,t);if(!(n==null||typeof n=="numbe...
function zE (line 282) | function zE(e,t){try{return vE(t,{reactCompat:!0})}catch(n){if(e.ignoreI...
function Lv (line 282) | function Lv(e,t,n){let r;if(!n)r={type:"Literal",value:t};else if(t.incl...
function _o (line 282) | function _o(e,t){const n=new at("Cannot handle MDX estrees without `crea...
function FE (line 282) | function FE(e){const t={};let n;for(n in e)Ph.call(e,n)&&(t[OE(n)]=e[n])...
function OE (line 282) | function OE(e){let t=e.replace(kE,VE);return t.slice(0,3)==="ms-"&&(t="-...
function VE (line 282) | function VE(e){return"-"+e.toLowerCase()}
function Dh (line 282) | function Dh(e,t){const n=$E,r=typeof n.includeImageAlt=="boolean"?n.incl...
function Rv (line 282) | function Rv(e,t,n){if(BE(e)){if("value"in e)return e.type==="html"&&!n?"...
function dg (line 282) | function dg(e,t,n){const r=[];let i=-1;for(;++i<e.length;)r[i]=Rv(e[i],t...
function BE (line 282) | function BE(e){return!!(e&&typeof e=="object")}
function Ih (line 282) | function Ih(e){const t="&"+e+";";fg.innerHTML=t;const n=fg.textContent;r...
function Lt (line 282) | function Lt(e,t,n,r){const i=e.length;let s=0,o;if(t<0?t=-t>i?0:i+t:t=t>...
function Wt (line 282) | function Wt(e,t){return e.length>0?(Lt(e,e.length,0,t),e):t}
function zv (line 282) | function zv(e){const t={};let n=-1;for(;++n<e.length;)HE(t,e[n]);return t}
function HE (line 282) | function HE(e,t){let n;for(n in t){const i=(hg.call(e,n)?e[n]:void 0)||(...
function UE (line 282) | function UE(e,t){let n=-1;const r=[];for(;++n<t.length;)(t[n].add==="aft...
function Fv (line 282) | function Fv(e,t){const n=Number.parseInt(e,t);return n<9||n===11||n>13&&...
function an (line 282) | function an(e){return e.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"")....
function Cl (line 282) | function Cl(e){return e!==null&&(e<32||e===127)}
function J (line 282) | function J(e){return e!==null&&e<-2}
function ve (line 282) | function ve(e){return e!==null&&(e<0||e===32)}
function ue (line 282) | function ue(e){return e===-2||e===-1||e===32}
function Nr (line 282) | function Nr(e){return t;function t(n){return n!==null&&n>-1&&e.test(Stri...
function ms (line 282) | function ms(e){const t=[];let n=-1,r=0,i=0;for(;++n<e.length;){const s=e...
function fe (line 282) | function fe(e,t,n,r){const i=r?r-1:Number.POSITIVE_INFINITY;let s=0;retu...
function KE (line 282) | function KE(e){const t=e.attempt(this.parser.constructs.contentInitial,r...
function QE (line 282) | function QE(e){const t=this,n=[];let r=0,i,s,o;return a;function a(v){if...
function ZE (line 282) | function ZE(e,t,n){return fe(e,e.attempt(this.parser.constructs.document...
function rs (line 282) | function rs(e){if(e===null||ve(e)||ni(e))return 1;if(cu(e))return 2}
function du (line 282) | function du(e,t,n){const r=[];let i=-1;for(;++i<e.length;){const s=e[i]....
function JE (line 282) | function JE(e,t){let n=-1,r,i,s,o,a,l,u,c;for(;++n<e.length;)if(e[n][0]=...
function e3 (line 282) | function e3(e,t){const n=this.parser.constructs.attentionMarkers.null,r=...
function mg (line 282) | function mg(e,t){e.column+=t,e.offset+=t,e._bufferIndex+=t}
function n3 (line 282) | function n3(e,t,n){let r=0;return i;function i(h){return e.enter("autoli...
function r3 (line 282) | function r3(e,t,n){return r;function r(s){return ue(s)?fe(e,i,"linePrefi...
function i3 (line 282) | function i3(e,t,n){const r=this;return i;function i(o){if(o===62){const ...
function s3 (line 282) | function s3(e,t,n){const r=this;return i;function i(o){return ue(o)?fe(e...
function o3 (line 282) | function o3(e){e.exit("blockQuote")}
function a3 (line 282) | function a3(e,t,n){return r;function r(s){return e.enter("characterEscap...
function l3 (line 282) | function l3(e,t,n){const r=this;let i=0,s,o;return a;function a(d){retur...
function u3 (line 282) | function u3(e,t,n){const r=this,i={partial:!0,tokenize:N};let s=0,o=0,a;...
function c3 (line 282) | function c3(e,t,n){const r=this;return i;function i(o){return o===null?n...
function f3 (line 282) | function f3(e,t,n){const r=this;return i;function i(u){return e.enter("c...
function h3 (line 282) | function h3(e,t,n){const r=this;return i;function i(o){return r.parser.l...
function m3 (line 282) | function m3(e){let t=e.length-4,n=3,r,i;if((e[n][1].type==="lineEnding"|...
function g3 (line 282) | function g3(e){return e!==96||this.events[this.events.length-1][1].type=...
function y3 (line 282) | function y3(e,t,n){let r=0,i,s;return o;function o(d){return e.enter("co...
class x3 (line 282) | class x3{constructor(t){this.left=t?[...t]:[],this.right=[]}get(t){if(t<...
method constructor (line 282) | constructor(t){this.left=t?[...t]:[],this.right=[]}
method get (line 282) | get(t){if(t<0||t>=this.left.length+this.right.length)throw new RangeEr...
method length (line 282) | get length(){return this.left.length+this.right.length}
method shift (line 282) | shift(){return this.setCursor(0),this.right.pop()}
method slice (line 282) | slice(t,n){const r=n??Number.POSITIVE_INFINITY;return r<this.left.leng...
method splice (line 282) | splice(t,n,r){const i=n||0;this.setCursor(Math.trunc(t));const s=this....
method pop (line 282) | pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}
method push (line 282) | push(t){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(t)}
method pushMany (line 282) | pushMany(t){this.setCursor(Number.POSITIVE_INFINITY),Cs(this.left,t)}
method unshift (line 282) | unshift(t){this.setCursor(0),this.right.push(t)}
method unshiftMany (line 282) | unshiftMany(t){this.setCursor(0),Cs(this.right,t.reverse())}
method setCursor (line 282) | setCursor(t){if(!(t===this.left.length||t>this.left.length&&this.right...
function Cs (line 282) | function Cs(e,t){let n=0;if(t.length<1e4)e.push(...t);else for(;n<t.leng...
function Bv (line 282) | function Bv(e){const t={};let n=-1,r,i,s,o,a,l,u;const c=new x3(e);for(;...
function v3 (line 282) | function v3(e,t){const n=e.get(t)[1],r=e.get(t)[2];let i=t-1;const s=[];...
function k3 (line 282) | function k3(e){return Bv(e),e}
function S3 (line 282) | function S3(e,t){let n;return r;function r(a){return e.enter("content"),...
function _3 (line 282) | function _3(e,t,n){const r=this;return i;function i(o){return e.exit("ch...
function Hv (line 282) | function Hv(e,t,n,r,i,s,o,a,l){const u=l||Number.POSITIVE_INFINITY;let c...
function Uv (line 282) | function Uv(e,t,n,r,i,s){const o=this;let a=0,l;return u;function u(h){r...
function Wv (line 282) | function Wv(e,t,n,r,i,s){let o;return a;function a(f){return f===34||f==...
function Js (line 282) | function Js(e,t){let n;return r;function r(i){return J(i)?(e.enter("line...
function N3 (line 282) | function N3(e,t,n){const r=this;let i;return s;function s(h){return e.en...
function T3 (line 282) | function T3(e,t,n){return r;function r(a){return ve(a)?Js(e,i)(a):n(a)}f...
function P3 (line 282) | function P3(e,t,n){return r;function r(s){return e.enter("hardBreakEscap...
function M3 (line 282) | function M3(e,t){let n=e.length-2,r=3,i,s;return e[r][1].type==="whitesp...
function D3 (line 282) | function D3(e,t,n){let r=0;return i;function i(c){return e.enter("atxHea...
function F3 (line 282) | function F3(e){let t=e.length;for(;t--&&!(e[t][0]==="enter"&&e[t][1].typ...
function O3 (line 282) | function O3(e,t,n){const r=this;let i,s,o,a,l;return u;function u(E){ret...
function V3 (line 282) | function V3(e,t,n){const r=this;return i;function i(o){return J(o)?(e.en...
function $3 (line 282) | function $3(e,t,n){return r;function r(i){return e.enter("lineEnding"),e...
function H3 (line 282) | function H3(e,t,n){const r=this;let i,s,o;return a;function a(k){return ...
function Y3 (line 282) | function Y3(e){let t=-1;const n=[];for(;++t<e.length;){const r=e[t][1];i...
function q3 (line 282) | function q3(e,t){let n=e.length,r=0,i,s,o,a;for(;n--;)if(i=e[n][1],s){if...
function K3 (line 282) | function K3(e,t,n){const r=this;let i=r.events.length,s,o;for(;i--;)if((...
function X3 (line 282) | function X3(e,t,n){return r;function r(d){return e.enter("resource"),e.e...
function Q3 (line 282) | function Q3(e,t,n){const r=this;return i;function i(a){return Uv.call(r,...
function Z3 (line 282) | function Z3(e,t,n){return r;function r(s){return e.enter("reference"),e....
function eN (line 282) | function eN(e,t,n){const r=this;return i;function i(a){return e.enter("l...
function nN (line 282) | function nN(e,t,n){const r=this;return i;function i(o){return e.enter("l...
function rN (line 282) | function rN(e,t){return n;function n(r){return e.enter("lineEnding"),e.c...
function iN (line 282) | function iN(e,t,n){let r=0,i;return s;function s(u){return e.enter("them...
function aN (line 282) | function aN(e,t,n){const r=this,i=r.events[r.events.length-1];let s=i&&i...
function lN (line 282) | function lN(e,t,n){const r=this;return r.containerState._closeFlow=void ...
function uN (line 282) | function uN(e,t,n){const r=this;return fe(e,i,"listItemIndent",r.contain...
function cN (line 282) | function cN(e){e.exit(this.containerState.type)}
function dN (line 282) | function dN(e,t,n){const r=this;return fe(e,i,"listItemPrefixWhitespace"...
function fN (line 282) | function fN(e,t){let n=e.length,r,i,s;for(;n--;)if(e[n][0]==="enter"){if...
function hN (line 282) | function hN(e,t,n){const r=this;let i;return s;function s(u){let c=r.eve...
function mN (line 282) | function mN(e){const t=this,n=e.attempt(Uo,r,e.attempt(this.parser.const...
function Gv (line 282) | function Gv(e){return{resolveAll:Yv(e==="text"?vN:void 0),tokenize:t};fu...
function Yv (line 282) | function Yv(e){return t;function t(n,r){let i=-1,s;for(;++i<=n.length;)s...
function vN (line 282) | function vN(e,t){let n=0;for(;++n<=e.length;)if((n===e.length||e[n][1].t...
function PN (line 282) | function PN(e,t,n){let r={_bufferIndex:-1,_index:0,line:n&&n.line||1,col...
function jN (line 282) | function jN(e,t){const n=t.start._index,r=t.start._bufferIndex,i=t.end._...
function MN (line 282) | function MN(e,t){let n=-1;const r=[];let i;for(;++n<e.length;){const s=e...
function DN (line 284) | function DN(e){const r={constructs:zv([AN,...(e||{}).extensions||[]]),co...
function IN (line 284) | function IN(e){for(;!Bv(e););return e}
function LN (line 284) | function LN(){let e=1,t="",n=!0,r;return i;function i(s,o,a){const l=[];...
function zN (line 284) | function zN(e){return e.replace(RN,FN)}
function FN (line 284) | function FN(e,t,n){if(t)return t;if(n.charCodeAt(0)===35){const i=n.char...
function ON (line 284) | function ON(e,t,n){return typeof t!="string"&&(n=t,t=void 0),VN(n)(IN(DN...
function VN (line 284) | function VN(e){const t={transforms:[],canContainEols:["emphasis","fragme...
function Kn (line 284) | function Kn(e){return{line:e.line,column:e.column,offset:e.offset}}
function Kv (line 284) | function Kv(e,t){let n=-1;for(;++n<t.length;){const r=t[n];Array.isArray...
function $N (line 284) | function $N(e,t){let n;for(n in t)if(qv.call(t,n))switch(n){case"canCont...
function bg (line 284) | function bg(e,t){throw e?new Error("Cannot close `"+e.type+"` ("+Zs({sta...
function BN (line 284) | function BN(e){const t=this;t.parser=n;function n(r){return ON(r,{...t.d...
function HN (line 284) | function HN(e,t){const n={type:"element",tagName:"blockquote",properties...
function UN (line 284) | function UN(e,t){const n={type:"element",tagName:"br",properties:{},chil...
function WN (line 285) | function WN(e,t){const n=t.value?t.value+`
function GN (line 286) | function GN(e,t){const n={type:"element",tagName:"del",properties:{},chi...
function YN (line 286) | function YN(e,t){const n={type:"element",tagName:"em",properties:{},chil...
function qN (line 286) | function qN(e,t){const n=typeof e.options.clobberPrefix=="string"?e.opti...
function KN (line 286) | function KN(e,t){const n={type:"element",tagName:"h"+t.depth,properties:...
function XN (line 286) | function XN(e,t){if(e.options.allowDangerousHtml){const n={type:"raw",va...
function Xv (line 286) | function Xv(e,t){const n=t.referenceType;let r="]";if(n==="collapsed"?r+...
function QN (line 286) | function QN(e,t){const n=String(t.identifier).toUpperCase(),r=e.definiti...
function ZN (line 286) | function ZN(e,t){const n={src:ms(t.url)};t.alt!==null&&t.alt!==void 0&&(...
function JN (line 286) | function JN(e,t){const n={type:"text",value:t.value.replace(/\r?\n|\r/g,...
function e4 (line 286) | function e4(e,t){const n=String(t.identifier).toUpperCase(),r=e.definiti...
function t4 (line 286) | function t4(e,t){const n={href:ms(t.url)};t.title!==null&&t.title!==void...
function n4 (line 286) | function n4(e,t,n){const r=e.all(t),i=n?r4(n):Qv(t),s={},o=[];if(typeof ...
function r4 (line 288) | function r4(e){let t=!1;if(e.type==="list"){t=e.spread||!1;const n=e.chi...
function Qv (line 288) | function Qv(e){const t=e.spread;return t??e.children.length>1}
function i4 (line 288) | function i4(e,t){const n={},r=e.all(t);let i=-1;for(typeof t.start=="num...
function s4 (line 288) | function s4(e,t){const n={type:"element",tagName:"p",properties:{},child...
function o4 (line 288) | function o4(e,t){const n={type:"root",children:e.wrap(e.all(t))};return ...
function a4 (line 288) | function a4(e,t){const n={type:"element",tagName:"strong",properties:{},...
function l4 (line 288) | function l4(e,t){const n=e.all(t),r=n.shift(),i=[];if(r){const o={type:"...
function u4 (line 288) | function u4(e,t,n){const r=n?n.children:void 0,s=(r?r.indexOf(t):1)===0?...
function c4 (line 288) | function c4(e,t){const n={type:"element",tagName:"td",properties:{},chil...
function d4 (line 288) | function d4(e){const t=String(e),n=/\r?\n|\r/g;let r=n.exec(t),i=0;const...
function _g (line 288) | function _g(e,t,n){let r=0,i=e.length;if(t){let s=e.codePointAt(r);for(;...
function f4 (line 288) | function f4(e,t){const n={type:"text",value:d4(String(t.value))};return ...
function h4 (line 288) | function h4(e,t){const n={type:"element",tagName:"hr",properties:{},chil...
function ga (line 288) | function ga(){}
function v4 (line 288) | function v4(e,t){const n=[{type:"text",value:"↩"}];return t>1&&n.push({t...
function w4 (line 288) | function w4(e,t){return"Back to reference "+(e+1)+(t>1?"-"+t:"")}
function b4 (line 288) | function b4(e){const t=typeof e.options.clobberPrefix=="string"?e.option...
function k4 (line 290) | function k4(e){const t=[];let n=-1;for(;++n<e.length;)t[n]=hu(e[n]);retu...
function S4 (line 290) | function S4(e){const t=e;return pu(n);function n(r){const i=r;let s;for(...
function _4 (line 290) | function _4(e){return pu(t);function t(n){return n&&n.type===e}}
function pu (line 290) | function pu(e){return t;function t(n,r,i){return!!(E4(n)&&e.call(this,n,...
function C4 (line 290) | function C4(){return!0}
function E4 (line 290) | function E4(e){return e!==null&&typeof e=="object"&&"type"in e}
function nw (line 290) | function nw(e,t,n,r){let i;typeof t=="function"&&typeof n!="function"?(r...
function A4 (line 290) | function A4(e){return Array.isArray(e)?e:typeof e=="number"?[N4,e]:e==nu...
function Vh (line 290) | function Vh(e,t,n,r){let i,s,o;typeof t=="function"&&typeof n!="function...
function j4 (line 290) | function j4(e,t){const n=t||P4,r=new Map,i=new Map,s=new Map,o={...p4,.....
function M4 (line 290) | function M4(e,t){e.position&&(t.position=wE(e))}
function D4 (line 290) | function D4(e,t){let n=t;if(e&&e.data){const r=e.data.hName,i=e.data.hCh...
function I4 (line 290) | function I4(e,t){const n=t.data||{},r="value"in t&&!(Qd.call(n,"hPropert...
function L4 (line 290) | function L4(e,t){const n=[];let r=-1;for(t&&n.push({type:"text",value:`
function Tg (line 293) | function Tg(e){let t=0,n=e.charCodeAt(t);for(;n===9||n===32;)t++,n=e.cha...
function Ag (line 293) | function Ag(e,t){const n=j4(e,t),r=n.one(e,void 0),i=b4(n),s=Array.isArr...
function R4 (line 294) | function R4(e,t){return e&&"run"in e?async function(n,r){const i=Ag(n,{f...
function Pg (line 294) | function Pg(e){if(e)throw e}
function Zd (line 294) | function Zd(e){if(typeof e!="object"||e===null)return!1;const t=Object.g...
function F4 (line 294) | function F4(){const e=[],t={run:n,use:r};return t;function n(...i){let s...
function O4 (line 294) | function O4(e,t){let n;return r;function r(...o){const a=e.length>o.leng...
function V4 (line 294) | function V4(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('...
function $4 (line 294) | function $4(e){if(Wo(e),e.length===0)return".";let t=-1,n=e.length,r;for...
function B4 (line 294) | function B4(e){Wo(e);let t=e.length,n=-1,r=0,i=-1,s=0,o;for(;t--;){const...
function H4 (line 294) | function H4(...e){let t=-1,n;for(;++t<e.length;)Wo(e[t]),e[t]&&(n=n===vo...
function U4 (line 294) | function U4(e){Wo(e);const t=e.codePointAt(0)===47;let n=W4(e,!t);return...
function W4 (line 294) | function W4(e,t){let n="",r=0,i=-1,s=0,o=-1,a,l;for(;++o<=e.length;){if(...
function Wo (line 294) | function Wo(e){if(typeof e!="string")throw new TypeError("Path must be a...
function Y4 (line 294) | function Y4(){return"/"}
function Jd (line 294) | function Jd(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href...
function q4 (line 294) | function q4(e){if(typeof e=="string")e=new URL(e);else if(!Jd(e)){const ...
function K4 (line 294) | function K4(e){if(e.hostname!==""){const r=new TypeError('File URL host ...
class iw (line 294) | class iw{constructor(t){let n;t?Jd(t)?n={path:t}:typeof t=="string"||X4(...
method constructor (line 294) | constructor(t){let n;t?Jd(t)?n={path:t}:typeof t=="string"||X4(t)?n={v...
method basename (line 294) | get basename(){return typeof this.path=="string"?mn.basename(this.path...
method basename (line 294) | set basename(t){gc(t,"basename"),mc(t,"basename"),this.path=mn.join(th...
method dirname (line 294) | get dirname(){return typeof this.path=="string"?mn.dirname(this.path):...
method dirname (line 294) | set dirname(t){zg(this.basename,"dirname"),this.path=mn.join(t||"",thi...
method extname (line 294) | get extname(){return typeof this.path=="string"?mn.extname(this.path):...
method extname (line 294) | set extname(t){if(mc(t,"extname"),zg(this.dirname,"extname"),t){if(t.c...
method path (line 294) | get path(){return this.history[this.history.length-1]}
method path (line 294) | set path(t){Jd(t)&&(t=q4(t)),gc(t,"path"),this.path!==t&&this.history....
method stem (line 294) | get stem(){return typeof this.path=="string"?mn.basename(this.path,thi...
method stem (line 294) | set stem(t){gc(t,"stem"),mc(t,"stem"),this.path=mn.join(this.dirname||...
method fail (line 294) | fail(t,n,r){const i=this.message(t,n,r);throw i.fatal=!0,i}
method info (line 294) | info(t,n,r){const i=this.message(t,n,r);return i.fatal=void 0,i}
method message (line 294) | message(t,n,r){const i=new at(t,n,r);return this.path&&(i.name=this.pa...
method toString (line 294) | toString(t){return this.value===void 0?"":typeof this.value=="string"?...
function mc (line 294) | function mc(e,t){if(e&&e.includes(mn.sep))throw new Error("`"+t+"` canno...
function gc (line 294) | function gc(e,t){if(!e)throw new Error("`"+t+"` cannot be empty")}
function zg (line 294) | function zg(e,t){if(!e)throw new Error("Setting `"+t+"` requires `path` ...
function X4 (line 294) | function X4(e){return!!(e&&typeof e=="object"&&"byteLength"in e&&"byteOf...
class $h (line 294) | class $h extends Q4{constructor(){super("copy"),this.Compiler=void 0,thi...
method constructor (line 294) | constructor(){super("copy"),this.Compiler=void 0,this.Parser=void 0,th...
method copy (line 294) | copy(){const t=new $h;let n=-1;for(;++n<this.attachers.length;){const ...
method data (line 294) | data(t,n){return typeof t=="string"?arguments.length===2?(vc("data",th...
method freeze (line 294) | freeze(){if(this.frozen)return this;const t=this;for(;++this.freezeInd...
method parse (line 294) | parse(t){this.freeze();const n=xa(t),r=this.parser||this.Parser;return...
method process (line 294) | process(t,n){const r=this;return this.freeze(),yc("process",this.parse...
method processSync (line 294) | processSync(t){let n=!1,r;return this.freeze(),yc("processSync",this.p...
method run (line 294) | run(t,n,r){Fg(t),this.freeze();const i=this.transformers;return!r&&typ...
method runSync (line 294) | runSync(t,n){let r=!1,i;return this.run(t,n,s),Og("runSync","run",r),i...
method stringify (line 294) | stringify(t,n){this.freeze();const r=xa(n),i=this.compiler||this.Compi...
method use (line 294) | use(t,...n){const r=this.attachers,i=this.namespace;if(vc("use",this.f...
function yc (line 294) | function yc(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+...
function xc (line 294) | function xc(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+...
function vc (line 294) | function vc(e,t){if(t)throw new Error("Cannot call `"+e+"` on a frozen p...
function Fg (line 294) | function Fg(e){if(!Zd(e)||typeof e.type!="string")throw new TypeError("E...
function Og (line 294) | function Og(e,t,n){if(!n)throw new Error("`"+e+"` finished async. Use `"...
function xa (line 294) | function xa(e){return eT(e)?e:new iw(e)}
function eT (line 294) | function eT(e){return!!(e&&typeof e=="object"&&"message"in e&&"messages"...
function tT (line 294) | function tT(e){return typeof e=="string"||nT(e)}
function nT (line 294) | function nT(e){return!!(e&&typeof e=="object"&&"byteLength"in e&&"byteOf...
function Bg (line 294) | function Bg(e){const t=oT(e),n=aT(e);return lT(t.runSync(t.parse(n),n),e)}
function oT (line 294) | function oT(e){const t=e.rehypePlugins||Vg,n=e.remarkPlugins||Vg,r=e.rem...
function aT (line 294) | function aT(e){const t=e.children||"",n=new iw;return typeof t=="string"...
function lT (line 294) | function lT(e,t){const n=t.allowedElements,r=t.allowElement,i=t.componen...
function uT (line 294) | function uT(e){const t=e.indexOf(":"),n=e.indexOf("?"),r=e.indexOf("#"),...
function Hg (line 294) | function Hg(e,t){const n=String(e);if(typeof t!="string")throw new TypeE...
function cT (line 294) | function cT(e){if(typeof e!="string")throw new TypeError("Expected a str...
function dT (line 294) | function dT(e,t,n){const i=hu((n||{}).ignore||[]),s=fT(t);let o=-1;for(;...
function fT (line 294) | function fT(e){const t=[];if(!Array.isArray(e))throw new TypeError("Expe...
function hT (line 294) | function hT(e){return typeof e=="string"?new RegExp(cT(e),"g"):e}
function pT (line 294) | function pT(e){return typeof e=="function"?e:function(){return e}}
function mT (line 294) | function mT(){return{transforms:[kT],enter:{literalAutolink:yT,literalAu...
function gT (line 294) | function gT(){return{unsafe:[{character:"@",before:"[+\\-.\\w]",after:"[...
function yT (line 294) | function yT(e){this.enter({type:"link",title:null,url:"",children:[]},e)}
function kc (line 294) | function kc(e){this.config.enter.autolinkProtocol.call(this,e)}
function xT (line 294) | function xT(e){this.config.exit.autolinkProtocol.call(this,e)}
function vT (line 294) | function vT(e){this.config.exit.data.call(this,e);const t=this.stack[thi...
function wT (line 294) | function wT(e){this.config.exit.autolinkEmail.call(this,e)}
function bT (line 294) | function bT(e){this.exit(e)}
function kT (line 294) | function kT(e){dT(e,[[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi,...
function ST (line 294) | function ST(e,t,n,r,i){let s="";if(!sw(i)||(/^w/i.test(t)&&(n=t+n,t="",s...
function _T (line 294) | function _T(e,t,n,r){return!sw(r,!0)||/[-\d_]$/.test(n)?!1:{type:"link",...
function CT (line 294) | function CT(e){const t=e.split(".");return!(t.length<2||t[t.length-1]&&(...
function ET (line 294) | function ET(e){const t=/[!"&'),.:;<>?\]}]+$/.exec(e);if(!t)return[e,void...
function sw (line 294) | function sw(e,t){const n=e.input.charCodeAt(e.index-1);return(e.index===...
function NT (line 294) | function NT(){this.buffer()}
function TT (line 294) | function TT(e){this.enter({type:"footnoteReference",identifier:"",label:...
function AT (line 294) | function AT(){this.buffer()}
function PT (line 294) | function PT(e){this.enter({type:"footnoteDefinition",identifier:"",label...
function jT (line 294) | function jT(e){const t=this.resume(),n=this.stack[this.stack.length-1];n...
function MT (line 294) | function MT(e){this.exit(e)}
function DT (line 294) | function DT(e){const t=this.resume(),n=this.stack[this.stack.length-1];n...
function IT (line 294) | function IT(e){this.exit(e)}
function LT (line 294) | function LT(){return"["}
function ow (line 294) | function ow(e,t,n,r){const i=n.createTracker(r);let s=i.move("[^");const...
function RT (line 294) | function RT(){return{enter:{gfmFootnoteCallString:NT,gfmFootnoteCall:TT,...
function zT (line 294) | function zT(e){let t=!1;return e&&e.firstLineBlank&&(t=!0),{handlers:{fo...
function FT (line 295) | function FT(e,t,n){return t===0?e:aw(e,t,n)}
function aw (line 295) | function aw(e,t,n){return(n?"":" ")+e}
function VT (line 295) | function VT(){return{canContainEols:["delete"],enter:{strikethrough:BT},...
function $T (line 295) | function $T(){return{unsafe:[{character:"~",inConstruct:"phrasing",notIn...
function BT (line 295) | function BT(e){this.enter({type:"delete",children:[]},e)}
function HT (line 295) | function HT(e){this.exit(e)}
function lw (line 295) | function lw(e,t,n,r){const i=n.createTracker(r),s=n.enter("strikethrough...
function UT (line 295) | function UT(){return"~"}
function WT (line 295) | function WT(e){return e.length}
function GT (line 295) | function GT(e,t){const n=t||{},r=(n.align||[]).concat(),i=n.stringLength...
function YT (line 296) | function YT(e){return e==null?"":String(e)}
function Ug (line 296) | function Ug(e){const t=typeof e=="string"?e.codePointAt(0):0;return t===...
function qT (line 296) | function qT(e,t,n,r){const i=n.enter("blockquote"),s=n.createTracker(r);...
function KT (line 296) | function KT(e,t,n){return">"+(n?"":" ")+e}
function XT (line 296) | function XT(e,t){return Wg(e,t.inConstruct,!0)&&!Wg(e,t.notInConstruct,!1)}
function Wg (line 296) | function Wg(e,t,n){if(typeof t=="string"&&(t=[t]),!t||t.length===0)retur...
function Gg (line 296) | function Gg(e,t,n,r){let i=-1;for(;++i<n.unsafe.length;)if(n.unsafe[i].c...
function QT (line 298) | function QT(e,t){const n=String(e);let r=n.indexOf(t),i=r,s=0,o=0;if(typ...
function ZT (line 298) | function ZT(e,t){return!!(t.options.fences===!1&&e.value&&!e.lang&&/[^ \...
function JT (line 298) | function JT(e){const t=e.options.fence||"`";if(t!=="`"&&t!=="~")throw ne...
function eA (line 298) | function eA(e,t,n,r){const i=JT(n),s=e.value||"",o=i==="`"?"GraveAccent"...
function tA (line 301) | function tA(e,t,n){return(n?"":" ")+e}
function Bh (line 301) | function Bh(e){const t=e.options.quote||'"';if(t!=='"'&&t!=="'")throw ne...
function nA (line 301) | function nA(e,t,n,r){const i=Bh(n),s=i==='"'?"Quote":
Condensed preview — 815 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,528K chars).
[
{
"path": ".github/workflows/document_deploy.yml",
"chars": 796,
"preview": "name: Deploy MkDocs site\n\non:\n push:\n branches:\n - main # 当推送到主分支时触发\n - vyokky/dev # 当推送到 vyokky_dev 分支时"
},
{
"path": ".gitignore",
"chars": 1274,
"preview": "# Ignore login file\n*.bin\n\n# Ignore Jupyter Notebook checkpoints\n.ipynb_checkpoints\n/test/*\n/testing/*\n/deprecated/*\n/te"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 444,
"preview": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://op"
},
{
"path": "CONTRIBUTING.md",
"chars": 1017,
"preview": "# Contributing\n\nThis project welcomes contributions and suggestions. Most contributions require you to\nagree to a Contri"
},
{
"path": "DISCLAIMER.md",
"chars": 2762,
"preview": "# Disclaimer: Code Execution and Data Handling Notice\n\nBy choosing to run the provided code, you acknowledge and agree t"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "Copyright (c) Microsoft Corporation.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "README.md",
"chars": 24828,
"preview": "<!-- markdownlint-disable MD033 MD041 -->\r\n\r\n<h1 align=\"center\">\r\n <b>UFO³</b> <img src=\"assets/logo3.png\" alt=\"UFO log"
},
{
"path": "README_ZH.md",
"chars": 17241,
"preview": "<!-- markdownlint-disable MD033 MD041 -->\n\n<h1 align=\"center\">\n <b>UFO³</b> <img src=\"assets/logo3.png\" alt=\"UFO logo\" "
},
{
"path": "SECURITY.md",
"chars": 2656,
"preview": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products an"
},
{
"path": "aip/__init__.py",
"chars": 3129,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAgent Interaction Protocol (AIP)\n\nA lightw"
},
{
"path": "aip/endpoints/__init__.py",
"chars": 499,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Endpoints\n\nProvides endpoint implement"
},
{
"path": "aip/endpoints/base.py",
"chars": 4122,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nBase AIP Endpoint\n\nProvides the foundation"
},
{
"path": "aip/endpoints/client_endpoint.py",
"chars": 4599,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nDevice Client Endpoint\n\nWraps the existing"
},
{
"path": "aip/endpoints/constellation_endpoint.py",
"chars": 5655,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nConstellation Client Endpoint\n\nWraps the e"
},
{
"path": "aip/endpoints/server_endpoint.py",
"chars": 4131,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nDevice Server Endpoint\n\nWraps the existing"
},
{
"path": "aip/extensions/__init__.py",
"chars": 320,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Extension Support\n\nProvides extension "
},
{
"path": "aip/extensions/base.py",
"chars": 1531,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nBase Extension Interface\n\nDefines the inte"
},
{
"path": "aip/extensions/middleware.py",
"chars": 4580,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Extension Middleware\n\nProvides ready-t"
},
{
"path": "aip/messages.py",
"chars": 18684,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAgent Interaction Protocol (AIP) - Message"
},
{
"path": "aip/protocol/__init__.py",
"chars": 666,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Protocol Layer\n\nImplements the core pr"
},
{
"path": "aip/protocol/base.py",
"chars": 20949,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nBase Protocol Implementation\n\nProvides the"
},
{
"path": "aip/protocol/command.py",
"chars": 2088,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nCommand Protocol\n\nHandles command executio"
},
{
"path": "aip/protocol/device_info.py",
"chars": 3498,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nDevice Info Protocol\n\nHandles device infor"
},
{
"path": "aip/protocol/heartbeat.py",
"chars": 4003,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nHeartbeat Protocol\n\nHandles periodic keepa"
},
{
"path": "aip/protocol/registration.py",
"chars": 6840,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nRegistration Protocol\n\nHandles agent regis"
},
{
"path": "aip/protocol/task_execution.py",
"chars": 9188,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nTask Execution Protocol\n\nHandles task assi"
},
{
"path": "aip/resilience/__init__.py",
"chars": 510,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Resilience Mechanisms\n\nProvides connec"
},
{
"path": "aip/resilience/heartbeat_manager.py",
"chars": 4730,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nHeartbeat Manager\n\nManages periodic heartb"
},
{
"path": "aip/resilience/reconnection.py",
"chars": 7337,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nReconnection Strategy\n\nImplements automati"
},
{
"path": "aip/resilience/timeout.py",
"chars": 2695,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nTimeout Manager\n\nHandles timeout enforceme"
},
{
"path": "aip/transport/__init__.py",
"chars": 645,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nAIP Transport Layer\n\nProvides transport ab"
},
{
"path": "aip/transport/adapters.py",
"chars": 7705,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nWebSocket Adapter Interface\n\nProvides a un"
},
{
"path": "aip/transport/base.py",
"chars": 3029,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nBase Transport Interface\n\nDefines the abst"
},
{
"path": "aip/transport/websocket.py",
"chars": 16812,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nWebSocket Transport Implementation\n\nImplem"
},
{
"path": "config/__init__.py",
"chars": 637,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nUFO² Configuration System\n\nModern, modular"
},
{
"path": "config/config_loader.py",
"chars": 22797,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nModern Configuration Loader for UFO³ and G"
},
{
"path": "config/config_schemas.py",
"chars": 30072,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n\"\"\"\nConfiguration Schema Definitions\n\nHybrid d"
},
{
"path": "config/galaxy/agent.yaml.template",
"chars": 1032,
"preview": "# Galaxy Constellation Agent Configuration\n\nCONSTELLATION_AGENT:\n REASONING_MODEL: False\n API_TYPE: \"openai\" # The AP"
},
{
"path": "config/galaxy/constellation.yaml",
"chars": 623,
"preview": "# Galaxy Constellation Configuration\n# This configuration defines runtime settings for constellation system\n\n# Constella"
},
{
"path": "config/galaxy/devices.yaml",
"chars": 2092,
"preview": "# Device Configuration - YAML Format\n# This configuration defines devices for the constellation\n# Runtime settings (cons"
},
{
"path": "config/ufo/agents.yaml.template",
"chars": 6166,
"preview": "# UFO Agent Configurations\n# All agent configurations for HOST, APP, BACKUP, EVALUATION, and OPERATOR agents\n# Copy this"
},
{
"path": "config/ufo/mcp.yaml",
"chars": 4051,
"preview": "# MCP (Model Context Protocol) Agent Configuration\n# This file defines the agents and their configurations for the MCP s"
},
{
"path": "config/ufo/prices.yaml",
"chars": 4256,
"preview": "# API Pricing Configuration\n# Source: https://openai.com/pricing\n# Prices in $ per 1000 tokens\n# Last updated: 2024-05-1"
},
{
"path": "config/ufo/rag.yaml",
"chars": 1302,
"preview": "# RAG (Retrieval Augmented Generation) Configuration\n\n# Offline Documentation RAG\nRAG_OFFLINE_DOCS: False # Whether to "
},
{
"path": "config/ufo/system.yaml",
"chars": 5055,
"preview": "# UFO System Configuration\n\n# LLM Parameters\nMAX_TOKENS: 2000 # The max token limit for the response completion\nMAX_RET"
},
{
"path": "config/ufo/third_party.yaml",
"chars": 1396,
"preview": "# Third-Party Agent Integration Configuration\n# This file configures external/third-party agents that extend UFO's capab"
},
{
"path": "dataflow/.gitignore",
"chars": 119,
"preview": "# Ignore files\ncache/\ncontrols_cache/\ncontroller/utils/\nconfig/config.yaml\ntasks/\nlogs/\nresults/\n_logs\n_results/\n*.zip\n"
},
{
"path": "dataflow/README.md",
"chars": 23651,
"preview": "<h1 align=\"center\">\n Large Action Models: From Inception to Implementation\n</h1>\n\n\n<div align=\"center\">\n\n[ Microsoft Corporation.\n# Licensed under the MIT License.\nfrom dataflow import dataflow\n\nif __name__ == \""
},
{
"path": "dataflow/config/config.py",
"chars": 1006,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom ufo.config import Config\n\n\nclass Config(C"
},
{
"path": "dataflow/config/config.yaml.template",
"chars": 3654,
"preview": "PREFILL_AGENT: {\n VISUAL_MODE: True, # Whether to use the visual mode\n\n API_TYPE: \"openai\" , # The API type, \"openai\" "
},
{
"path": "dataflow/config/config_dev.yaml",
"chars": 2447,
"preview": "version: 0.1\n\nCONTROL_BACKEND: \"uia\" # The list of backend for control action, currently we support uia and win32, \nCON"
},
{
"path": "dataflow/data_flow_controller.py",
"chars": 17637,
"preview": "import os\nimport time\nimport traceback\nfrom enum import Enum\nfrom typing import Any, Dict, Optional, List\nfrom jsonschem"
},
{
"path": "dataflow/dataflow.py",
"chars": 3793,
"preview": "import argparse\nimport os\nimport traceback\nfrom ufo.utils import print_with_color\nfrom dataflow.config.config import Con"
},
{
"path": "dataflow/env/env_manager.py",
"chars": 7400,
"preview": "import logging\nimport platform\nimport re\nfrom time import sleep\nfrom typing import Optional, Tuple, Dict, TYPE_CHECKING,"
},
{
"path": "dataflow/execution/agent/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/execution/agent/execute_agent.py",
"chars": 786,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom ufo.agents.agent.app_agent import AppAgen"
},
{
"path": "dataflow/execution/agent/execute_eval_agent.py",
"chars": 2106,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom typing import Optional\n\nfrom dataflow.pro"
},
{
"path": "dataflow/execution/workflow/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/execution/workflow/execute_flow.py",
"chars": 14084,
"preview": "import os\nimport time\nfrom typing import Any, Dict, List, Tuple\n\nfrom dataflow.config.config import Config as Instantiat"
},
{
"path": "dataflow/instantiation/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/instantiation/agent/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/instantiation/agent/filter_agent.py",
"chars": 2647,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom typing import List\n\nfrom dataflow.prompte"
},
{
"path": "dataflow/instantiation/agent/prefill_agent.py",
"chars": 3055,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom typing import Dict, List\n\nfrom dataflow.p"
},
{
"path": "dataflow/instantiation/agent/template_agent.py",
"chars": 2523,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nfrom typing import Dict, List\n\nfrom dataflow.p"
},
{
"path": "dataflow/instantiation/workflow/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/instantiation/workflow/choose_template_flow.py",
"chars": 9316,
"preview": "import json\nimport os\nimport random\nimport time\nimport warnings\nfrom datetime import datetime\nfrom pathlib import Path\nf"
},
{
"path": "dataflow/instantiation/workflow/filter_flow.py",
"chars": 5177,
"preview": "import json\nimport logging\nimport os\nimport time\nfrom typing import Dict, Tuple, Any\n\nfrom dataflow.config.config import"
},
{
"path": "dataflow/instantiation/workflow/prefill_flow.py",
"chars": 10617,
"preview": "import json\nimport logging\nimport os\nimport time\nfrom typing import Any, Dict, List, Tuple\n\nfrom dataflow.config.config "
},
{
"path": "dataflow/prompter/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/prompter/execution/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/prompter/execution/execute_eval_prompter.py",
"chars": 1583,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nimport json\nimport os\nfrom typing import Dict,"
},
{
"path": "dataflow/prompter/instantiation/__init__.py",
"chars": 72,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License."
},
{
"path": "dataflow/prompter/instantiation/filter_prompter.py",
"chars": 5118,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nimport json\nfrom typing import Dict, List\n\nfro"
},
{
"path": "dataflow/prompter/instantiation/prefill_prompter.py",
"chars": 5946,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nimport json\nimport os\nfrom typing import Dict,"
},
{
"path": "dataflow/prompter/instantiation/template_prompter.py",
"chars": 3283,
"preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\nimport base64\nimport mimetypes\nimport os\nfrom "
},
{
"path": "dataflow/prompts/instantiation/visual/filter.yaml",
"chars": 1878,
"preview": "version: 1.0\n\nsystem: |-\n You are a task judge, will be provided with a task in the <Task:>. You need to judge whether "
},
{
"path": "dataflow/prompts/instantiation/visual/prefill.yaml",
"chars": 7971,
"preview": "version: 1.0\n\nsystem: |-\n You are a Agent Task Creator and planer.\n You will receive a <Given Task> that is abstract a"
},
{
"path": "dataflow/prompts/instantiation/visual/prefill_example.yaml",
"chars": 2947,
"preview": "\nversion: 1.0\n\nexample1: \n Request: |-\n <Given Task:> Delete Text in document.\n Response:\n observation: |-\n "
},
{
"path": "dataflow/prompts/instantiation/visual/template.yaml",
"chars": 1305,
"preview": "version: 1.0\n\nsystem: |-\n You are a Word operator expert and you can easily perform any word-related operations.\n - Wh"
},
{
"path": "dataflow/schema/execution_schema.json",
"chars": 4137,
"preview": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"unique_id\": { \"type"
},
{
"path": "dataflow/schema/instantiation_schema.json",
"chars": 3215,
"preview": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"unique_id\": { \"type"
},
{
"path": "dataflow/templates/word/description.json",
"chars": 290,
"preview": "{\n \"1.docx\":\"A doc with a rectangle shape\",\n \"2.docx\":\"A doc with a line of text\",\n \"3.docx\":\"A doc with a char"
},
{
"path": "documents/docs/about/CODE_OF_CONDUCT.md",
"chars": 444,
"preview": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://op"
},
{
"path": "documents/docs/about/CONTRIBUTING.md",
"chars": 1022,
"preview": "# Contributing\n\nThis project welcomes contributions and suggestions. Most contributions require you to\nagree to a Contri"
},
{
"path": "documents/docs/about/DISCLAIMER.md",
"chars": 2762,
"preview": "# Disclaimer: Code Execution and Data Handling Notice\n\nBy choosing to run the provided code, you acknowledge and agree t"
},
{
"path": "documents/docs/about/LICENSE.md",
"chars": 1078,
"preview": "Copyright (c) Microsoft Corporation.\n\n## MIT License\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "documents/docs/about/SUPPORT.md",
"chars": 636,
"preview": "# Support\n\n## How to file issues and get help \n\nThis project uses GitHub Issues to track bugs and feature requests. Ple"
},
{
"path": "documents/docs/aip/endpoints.md",
"chars": 15003,
"preview": "# AIP Endpoints\n\nEndpoints combine protocol, transport, and resilience components to provide production-ready AIP commun"
},
{
"path": "documents/docs/aip/messages.md",
"chars": 18749,
"preview": "# AIP Message Reference\n\nAIP uses **Pydantic-based messages** for automatic validation, serialization, and type safety. "
},
{
"path": "documents/docs/aip/overview.md",
"chars": 18067,
"preview": "# Agent Interaction Protocol (AIP)\n\nThe orchestration model requires a communication substrate that remains **correct un"
},
{
"path": "documents/docs/aip/protocols.md",
"chars": 18466,
"preview": "# AIP Protocol Reference\n\n## Protocol Stack Overview\n\nAIP uses a three-layer architecture where specialized protocols ha"
},
{
"path": "documents/docs/aip/resilience.md",
"chars": 17911,
"preview": "# AIP Resilience\n\nAIP's resilience layer ensures stable communication and consistent orchestration across distributed ag"
},
{
"path": "documents/docs/aip/transport.md",
"chars": 18531,
"preview": "# AIP Transport Layer\n\nThe transport layer provides a pluggable abstraction for AIP's network communication, decoupling "
},
{
"path": "documents/docs/choose_path.md",
"chars": 13893,
"preview": "# Choosing Your Path: UFO² or UFO³ Galaxy?\n\nNot sure which UFO framework to use? This guide will help you make the right"
},
{
"path": "documents/docs/client/computer.md",
"chars": 17034,
"preview": "# Computer\n\nThe **Computer** class is the core execution layer of the UFO client. It manages MCP (Model Context Protocol"
},
{
"path": "documents/docs/client/computer_manager.md",
"chars": 15822,
"preview": "# Computer Manager & Computer\n\nThe **Computer Manager** orchestrates multiple **Computer** instances, each representing "
},
{
"path": "documents/docs/client/device_info.md",
"chars": 13090,
"preview": "# 📱 Device Info Provider\n\nThe **Device Info Provider** collects comprehensive system information from client devices dur"
},
{
"path": "documents/docs/client/mcp_integration.md",
"chars": 13561,
"preview": "# 🔌 MCP Integration\n\n**MCP (Model Context Protocol)** provides the tool execution layer in UFO clients, enabling agents "
},
{
"path": "documents/docs/client/overview.md",
"chars": 27554,
"preview": "# UFO Client Overview\n\nThe **UFO Client** runs on target devices and serves as the **execution layer** of UFO's distribu"
},
{
"path": "documents/docs/client/quick_start.md",
"chars": 25784,
"preview": "# ⚡ Quick Start\n\nGet your device connected to the UFO Agent Server and start executing tasks in minutes. No complex setu"
},
{
"path": "documents/docs/client/ufo_client.md",
"chars": 22223,
"preview": "# 🎯 UFO Client\n\nThe **UFO Client** is the execution engine that receives commands from the server, routes them to approp"
},
{
"path": "documents/docs/client/websocket_client.md",
"chars": 32794,
"preview": "# 🔌 WebSocket Client\n\nThe **WebSocket Client** implements the **AIP (Agent Interaction Protocol)** for reliable, bidirec"
},
{
"path": "documents/docs/configuration/models/azure_openai.md",
"chars": 3836,
"preview": "# Azure OpenAI (AOAI)\n\n## Step 1: Create Azure OpenAI Resource\n\nTo use the Azure OpenAI API, create an account on the [A"
},
{
"path": "documents/docs/configuration/models/claude.md",
"chars": 2638,
"preview": "# Anthropic Claude\n\n## Step 1: Obtain API Key\n\nTo use the Claude API, create an account on the [Anthropic Console](https"
},
{
"path": "documents/docs/configuration/models/custom_model.md",
"chars": 4077,
"preview": "# Customized LLM Models\n\nUFO supports and welcomes the integration of custom LLM models. If you have a custom LLM model "
},
{
"path": "documents/docs/configuration/models/deepseek.md",
"chars": 2065,
"preview": "# DeepSeek Model\n\n## Step 1: Obtain API Key\n\nDeepSeek is developed by DeepSeek AI. To use DeepSeek models, go to [DeepSe"
},
{
"path": "documents/docs/configuration/models/gemini.md",
"chars": 3002,
"preview": "# Google Gemini\n\n## Step 1: Obtain API Key\n\nTo use the Google Gemini API, create an account on [Google AI Studio](https:"
},
{
"path": "documents/docs/configuration/models/ollama.md",
"chars": 2876,
"preview": "# Ollama\n\n## Step 1: Install and Start Ollama\n\nGo to [Ollama](https://github.com/jmorganca/ollama) and follow the instal"
},
{
"path": "documents/docs/configuration/models/openai.md",
"chars": 2491,
"preview": "# OpenAI\n\n## Step 1: Obtain API Key\n\nTo use the OpenAI API, create an account on the [OpenAI website](https://platform.o"
},
{
"path": "documents/docs/configuration/models/operator.md",
"chars": 3623,
"preview": "# OpenAI CUA (Operator)\n\nThe [Operator](https://openai.com/index/computer-using-agent/) is a specialized agentic model t"
},
{
"path": "documents/docs/configuration/models/overview.md",
"chars": 3598,
"preview": "# Supported Models\n\nUFO supports a wide variety of LLM models and APIs. You can configure different models for `HOST_AGE"
},
{
"path": "documents/docs/configuration/models/qwen.md",
"chars": 2136,
"preview": "# Qwen Model\n\n## Step 1: Obtain API Key\n\nQwen (Tongyi Qianwen) is developed by Alibaba DAMO Academy. To use Qwen models,"
},
{
"path": "documents/docs/configuration/system/agents_config.md",
"chars": 14772,
"preview": "# Agent Configuration (agents.yaml)\n\nConfigure all LLM models and agent-specific settings for UFO². Each agent type can "
},
{
"path": "documents/docs/configuration/system/extending.md",
"chars": 8870,
"preview": "# Extending Configuration\n\nThis guide shows you how to add custom configuration options to UFO2.\n\n**Three Ways to Extend"
},
{
"path": "documents/docs/configuration/system/galaxy_agent.md",
"chars": 15162,
"preview": "# Galaxy Constellation Agent Configuration\n\n**agent.yaml** configures the **Constellation Agent** - the AI agent respons"
},
{
"path": "documents/docs/configuration/system/galaxy_constellation.md",
"chars": 13948,
"preview": "# Galaxy Constellation Runtime Configuration\n\n**constellation.yaml** defines constellation-wide runtime settings that co"
},
{
"path": "documents/docs/configuration/system/galaxy_devices.md",
"chars": 21899,
"preview": "# Galaxy Devices Configuration\n\nDevice configuration in **devices.yaml** defines the constellation's device agents, prov"
},
{
"path": "documents/docs/configuration/system/mcp_reference.md",
"chars": 4816,
"preview": "# MCP Configuration Reference\n\nThis document provides a quick reference for MCP (Model Context Protocol) server configur"
},
{
"path": "documents/docs/configuration/system/migration.md",
"chars": 11599,
"preview": "# Configuration Migration Guide\n\nThis guide helps you migrate from the legacy configuration system (`ufo/config/config."
},
{
"path": "documents/docs/configuration/system/overview.md",
"chars": 13629,
"preview": "# Configuration Architecture\n\nUFO² features a modern, modular configuration system designed for flexibility, maintainabi"
},
{
"path": "documents/docs/configuration/system/prices_config.md",
"chars": 5964,
"preview": "# Pricing Configuration (prices.yaml)\n\nConfigure token pricing for different LLM models to track and estimate API costs "
},
{
"path": "documents/docs/configuration/system/rag_config.md",
"chars": 14916,
"preview": "# RAG Configuration (rag.yaml)\n\nConfigure Retrieval-Augmented Generation (RAG) to enhance UFO² with external knowledge s"
},
{
"path": "documents/docs/configuration/system/system_config.md",
"chars": 17684,
"preview": "# System Configuration (system.yaml)\n\nConfigure UFO²'s runtime behavior, execution limits, control backends, logging, an"
},
{
"path": "documents/docs/configuration/system/third_party_config.md",
"chars": 12116,
"preview": "# Third-Party Agent Configuration (third_party.yaml)\n\nConfigure third-party agents that extend UFO²'s capabilities beyon"
},
{
"path": "documents/docs/faq.md",
"chars": 15692,
"preview": "# Frequently Asked Questions (FAQ)\n\nQuick answers to common questions about UFO³ Galaxy, UFO², Linux Agents, and general"
},
{
"path": "documents/docs/galaxy/agent_registration/agent_profile.md",
"chars": 25134,
"preview": "# 📊 AgentProfile - Comprehensive Agent Representation\n\nThe **AgentProfile** is a multi-source data structure that consol"
},
{
"path": "documents/docs/galaxy/agent_registration/device_registry.md",
"chars": 26956,
"preview": "# 🗄️ DeviceRegistry - Device Data Management\n\n## 📋 Overview\n\nThe **DeviceRegistry** is a focused component that manages "
},
{
"path": "documents/docs/galaxy/agent_registration/overview.md",
"chars": 21649,
"preview": "# 🌟 Agent Registration & Profiling - Overview\n\n**Agent Registration** is the cornerstone of the AIP (Agent Interaction P"
},
{
"path": "documents/docs/galaxy/agent_registration/registration_flow.md",
"chars": 28293,
"preview": "# 🔄 Registration Flow - Complete Process Guide\n\n## 📋 Overview\n\nThe registration flow transforms a device configuration e"
},
{
"path": "documents/docs/galaxy/client/aip_integration.md",
"chars": 31007,
"preview": "# AIP Protocol Integration\n\nThe Agent Interaction Protocol (AIP) is the communication protocol used throughout Galaxy Cl"
},
{
"path": "documents/docs/galaxy/client/components.md",
"chars": 22671,
"preview": "# Galaxy Client Components\n\nGalaxy Client is built from focused, single-responsibility components that work together to "
},
{
"path": "documents/docs/galaxy/client/constellation_client.md",
"chars": 20446,
"preview": "# ConstellationClient Reference\n\nConstellationClient is the device coordination layer in Galaxy Client. It provides a cl"
},
{
"path": "documents/docs/galaxy/client/device_manager.md",
"chars": 34436,
"preview": "# DeviceManager Reference\n\nDeviceManager is the connection orchestration layer in Galaxy Client. While ConstellationClie"
},
{
"path": "documents/docs/galaxy/client/galaxy_client.md",
"chars": 21584,
"preview": "# GalaxyClient Reference\n\nGalaxyClient is an optional session management wrapper on top of ConstellationClient. It provi"
},
{
"path": "documents/docs/galaxy/client/overview.md",
"chars": 23025,
"preview": "# Galaxy Client Overview\n\nGalaxy Client is the client-side layer responsible for multi-device coordination in the UFO³ f"
},
{
"path": "documents/docs/galaxy/constellation/constellation_editor.md",
"chars": 22874,
"preview": "# ConstellationEditor — Interactive DAG Editor\n\n---\n\n## 📋 Overview\n\n**ConstellationEditor** provides a high-level, comma"
},
{
"path": "documents/docs/galaxy/constellation/overview.md",
"chars": 13310,
"preview": "# Task Constellation — Overview\n\n<div align=\"center\">\n <img src=\"/img/task_constellation.png\" alt=\"Task Constellation D"
},
{
"path": "documents/docs/galaxy/constellation/task_constellation.md",
"chars": 21375,
"preview": "# TaskConstellation — DAG Orchestrator\n\n## Overview\n\n**TaskConstellation** is the complete DAG (Directed Acyclic Graph) "
},
{
"path": "documents/docs/galaxy/constellation/task_star.md",
"chars": 15470,
"preview": "# TaskStar — Atomic Execution Unit\n\n## Overview\n\n**TaskStar** represents the atomic unit of computation in UFO Galaxy—th"
},
{
"path": "documents/docs/galaxy/constellation/task_star_line.md",
"chars": 19110,
"preview": "# TaskStarLine — Dependency Relationship\n\n## Overview\n\n**TaskStarLine** represents a directed dependency relationship be"
},
{
"path": "documents/docs/galaxy/constellation_agent/command.md",
"chars": 21204,
"preview": "# Constellation MCP Server — Structured Task Management\n\n## Overview\n\nThe **Constellation MCP Server** provides a standa"
},
{
"path": "documents/docs/galaxy/constellation_agent/overview.md",
"chars": 21023,
"preview": "# Constellation Agent — The Centralized Constellation Weaver\n\nThe **Constellation Agent** serves as the central intellig"
},
{
"path": "documents/docs/galaxy/constellation_agent/state.md",
"chars": 20642,
"preview": "# Constellation Agent State Machine\n\nThe Constellation Agent's finite-state machine provides deterministic lifecycle man"
},
{
"path": "documents/docs/galaxy/constellation_agent/strategy.md",
"chars": 31574,
"preview": "# Processing Strategy Pattern\n\n## Overview\n\nThe Constellation Agent employs a sophisticated **multi-phase processing arc"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/api_reference.md",
"chars": 21116,
"preview": "# API Reference\n\n## Overview\n\nThis document provides comprehensive API documentation for the Constellation Orchestrator "
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/asynchronous_scheduling.md",
"chars": 19822,
"preview": "# Asynchronous Scheduling\n\n## Overview\n\nAt the core of the Constellation Orchestrator lies a fully **asynchronous schedu"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/batched_editing.md",
"chars": 15226,
"preview": "# Batched Constellation Editing\n\n## Overview\n\nFrequent LLM-driven edits can introduce significant overhead if processed "
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/consistency_guarantees.md",
"chars": 18304,
"preview": "# Consistency and Safety Guarantees\n\n## Overview\n\nSince the TaskConstellation may be dynamically rewritten by an LLM-bas"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/constellation_manager.md",
"chars": 20754,
"preview": "# Constellation Manager\n\n## Overview\n\nThe `ConstellationManager` is a companion component to the `TaskConstellationOrche"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/event_driven_coordination.md",
"chars": 16743,
"preview": "# Event-Driven Coordination\n\n## Overview\n\nTraditional DAG schedulers rely on **polling** or **global checkpoints** to de"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/overview.md",
"chars": 9460,
"preview": "# Constellation Orchestrator Overview\n\n## Introduction\n\nThe **Constellation Orchestrator** is the execution engine at th"
},
{
"path": "documents/docs/galaxy/constellation_orchestrator/safe_assignment_locking.md",
"chars": 18016,
"preview": "# Safe Assignment Locking\n\n## Overview\n\nWhile asynchronous execution maximizes efficiency, it introduces correctness cha"
},
{
"path": "documents/docs/galaxy/evaluation/performance_metrics.md",
"chars": 21733,
"preview": "# Performance Metrics and Logging\n\nGalaxy provides comprehensive performance monitoring and metrics collection throughou"
},
{
"path": "documents/docs/galaxy/evaluation/result_json.md",
"chars": 25564,
"preview": "# Result JSON Format Reference\n\nGalaxy automatically saves comprehensive execution results to `result.json` after each s"
},
{
"path": "documents/docs/galaxy/evaluation/trajectory_report.md",
"chars": 24855,
"preview": "# Galaxy Trajectory Report\n\n## Overview\n\nThe **Galaxy Trajectory Report** (`output.md`) is an automatically generated co"
},
{
"path": "documents/docs/galaxy/observer/agent_output_observer.md",
"chars": 15720,
"preview": "# Agent Output Observer\n\nThe **AgentOutputObserver** handles real-time display of agent responses and actions. It listen"
},
{
"path": "documents/docs/galaxy/observer/event_system.md",
"chars": 17202,
"preview": "# Event System Core\n\nThe Event System Core provides the foundational infrastructure for event-driven communication in th"
},
{
"path": "documents/docs/galaxy/observer/metrics_observer.md",
"chars": 19237,
"preview": "# Session Metrics Observer\n\nThe **SessionMetricsObserver** collects comprehensive performance metrics and statistics dur"
},
{
"path": "documents/docs/galaxy/observer/overview.md",
"chars": 14176,
"preview": "# Observer System — Overview\n\nThe **Observer System** in UFO Galaxy implements an event-driven architecture that enables"
},
{
"path": "documents/docs/galaxy/observer/progress_observer.md",
"chars": 14544,
"preview": "# Constellation Progress Observer\n\nThe **ConstellationProgressObserver** is responsible for tracking task execution prog"
},
{
"path": "documents/docs/galaxy/observer/synchronizer.md",
"chars": 15541,
"preview": "# Constellation Modification Synchronizer\n\nThe **ConstellationModificationSynchronizer** prevents race conditions betwee"
},
{
"path": "documents/docs/galaxy/observer/visualization_observer.md",
"chars": 13991,
"preview": "# DAG Visualization Observer\n\nThe **DAGVisualizationObserver** provides real-time visual feedback during constellation e"
},
{
"path": "documents/docs/galaxy/overview.md",
"chars": 24553,
"preview": "# UFO³ — Weaving the Digital Agent Galaxy\n\n<div align=\"center\">\n <img src=\"/img/poster.png\" alt=\"UFO³ Galaxy Concept\" s"
},
{
"path": "documents/docs/galaxy/webui.md",
"chars": 46315,
"preview": "# Galaxy WebUI\n\nThe **Galaxy WebUI** is a modern, interactive web interface for the UFO³ Galaxy Framework. It provides r"
},
{
"path": "documents/docs/getting_started/migration_ufo2_to_galaxy.md",
"chars": 20377,
"preview": "# Migration Guide: UFO² to UFO³ Galaxy\n\nThis guide helps you understand the evolution from **UFO²** (Desktop AgentOS) to"
},
{
"path": "documents/docs/getting_started/more_guidance.md",
"chars": 13259,
"preview": "# More Guidance\n\nThis page provides additional guidance and resources for different user types and use cases.\n\n---\n\n## 🎯"
},
{
"path": "documents/docs/getting_started/quick_start_galaxy.md",
"chars": 22005,
"preview": "# Quick Start Guide - UFO³ Galaxy\n\nWelcome to **UFO³ Galaxy** – the Multi-Device AgentOS! This guide will help you orche"
},
{
"path": "documents/docs/getting_started/quick_start_linux.md",
"chars": 27180,
"preview": "# ⚡ Quick Start: Linux Agent\n\nGet your Linux device running as a UFO³ device agent in 5 minutes. This guide walks you th"
},
{
"path": "documents/docs/getting_started/quick_start_mobile.md",
"chars": 37523,
"preview": "# ⚡ Quick Start: Mobile Agent\n\nGet your Android device running as a UFO³ device agent in 10 minutes. This guide walks yo"
},
{
"path": "documents/docs/getting_started/quick_start_ufo2.md",
"chars": 11630,
"preview": "# Quick Start Guide\n\nWelcome to **UFO²** – the Desktop AgentOS! This guide will help you get started with UFO² in just a"
},
{
"path": "documents/docs/index.md",
"chars": 21750,
"preview": "# Welcome to UFO³ Documentation\n\n<div align=\"center\">\n <h1>\n <b>UFO³</b> <img src=\"./img/logo3.png\" alt=\"UFO logo\" w"
},
{
"path": "documents/docs/infrastructure/agents/agent_types.md",
"chars": 40044,
"preview": "# Platform-Specific Agent Implementations\n\nThis document describes how the unified three-layer Device Agent architecture"
},
{
"path": "documents/docs/infrastructure/agents/design/blackboard.md",
"chars": 2330,
"preview": "# Agent Blackboard\n\nThe `Blackboard` is a shared memory space visible to all agents in the UFO framework. It stores info"
},
{
"path": "documents/docs/infrastructure/agents/design/command.md",
"chars": 31172,
"preview": "# Command Layer (Level-3 System Interface)\n\nThe **Command Layer** provides atomic, deterministic system operations that "
},
{
"path": "documents/docs/infrastructure/agents/design/memory.md",
"chars": 23045,
"preview": "# Memory System\n\nThe Memory System provides both short-term and long-term memory capabilities for Device Agents in UFO3."
},
{
"path": "documents/docs/infrastructure/agents/design/processor.md",
"chars": 33703,
"preview": "# Strategy Layer: Processor (Level-2)\n\nThe **Processor** is the core component of the **Strategy Layer (Level-2)**, prov"
},
{
"path": "documents/docs/infrastructure/agents/design/prompter.md",
"chars": 15466,
"preview": "# Agent Prompter\n\nThe `Prompter` is a key component of the UFO framework, responsible for constructing prompts for the L"
},
{
"path": "documents/docs/infrastructure/agents/design/state.md",
"chars": 25829,
"preview": "# State Layer (Level-1 FSM)\n\nThe **State Layer** is the top-level control structure governing device agent lifecycle. It"
},
{
"path": "documents/docs/infrastructure/agents/design/strategy.md",
"chars": 33303,
"preview": "# Processing Strategies\n\n**ProcessingStrategy** classes are the fundamental building blocks of agent execution logic. Ea"
},
{
"path": "documents/docs/infrastructure/agents/overview.md",
"chars": 28988,
"preview": "# Device Agent Architecture\n\nDevice Agents are the execution engines of UFO3's multi-device orchestration system. Each d"
},
{
"path": "documents/docs/infrastructure/agents/server_client_architecture.md",
"chars": 25677,
"preview": "# Server-Client Architecture\n\nDevice agents in UFO are partitioned into **server** and **client** components, separating"
},
{
"path": "documents/docs/infrastructure/modules/context.md",
"chars": 14748,
"preview": "# Context\n\nThe **Context** object is a type-safe shared state container that persists conversation state across all Roun"
},
{
"path": "documents/docs/infrastructure/modules/dispatcher.md",
"chars": 30027,
"preview": "# Command Dispatcher\n\nThe **Command Dispatcher** is the bridge between agent decisions and actual execution, routing com"
},
{
"path": "documents/docs/infrastructure/modules/overview.md",
"chars": 22227,
"preview": "# Module System Overview\n\nThe **Module System** is the core execution engine of UFO, orchestrating the complete lifecycl"
},
{
"path": "documents/docs/infrastructure/modules/platform_sessions.md",
"chars": 12620,
"preview": "# Platform-Specific Sessions\n\n**WindowsBaseSession** and **LinuxBaseSession** provide platform-specific base classes wit"
},
{
"path": "documents/docs/infrastructure/modules/round.md",
"chars": 16596,
"preview": "# Round\n\nA **Round** is a single request-response cycle within a Session, orchestrating agents through a state machine t"
},
{
"path": "documents/docs/infrastructure/modules/session.md",
"chars": 20323,
"preview": "# Session\n\nA **Session** is a continuous conversation instance between the user and UFO, managing multiple rounds of int"
},
{
"path": "documents/docs/infrastructure/modules/session_pool.md",
"chars": 26055,
"preview": "# Session Factory & Pool\n\nThe **SessionFactory** and **SessionPool** classes provide platform-aware session creation and"
},
{
"path": "documents/docs/javascripts/mermaid-init.js",
"chars": 890,
"preview": "// Initialize Mermaid for ReadTheDocs theme\n(function() {\n // Wait for DOM to be ready\n if (document.readyState =="
},
{
"path": "documents/docs/linux/as_galaxy_device.md",
"chars": 14987,
"preview": "# Using Linux Agent as Galaxy Device\n\nConfigure Linux Agent as a sub-agent in UFO's Galaxy framework to enable cross-pla"
},
{
"path": "documents/docs/linux/commands.md",
"chars": 11894,
"preview": "# LinuxAgent MCP Commands\n\nLinuxAgent interacts with Linux systems through MCP (Model Context Protocol) tools provided b"
},
{
"path": "documents/docs/linux/overview.md",
"chars": 6120,
"preview": "# LinuxAgent: CLI Task Executor\n\n**LinuxAgent** is a specialized lightweight agent designed for executing command-line i"
},
{
"path": "documents/docs/linux/state.md",
"chars": 9260,
"preview": "# LinuxAgent State Machine\n\nLinuxAgent uses a **3-state finite state machine (FSM)** to manage CLI task execution flow. "
},
{
"path": "documents/docs/linux/strategy.md",
"chars": 14763,
"preview": "# LinuxAgent Processing Strategy\n\nLinuxAgent executes a **3-phase processing pipeline** in the **CONTINUE** state. Each "
},
{
"path": "documents/docs/mcp/action.md",
"chars": 13629,
"preview": "# Action Servers\n\n## Overview\n\n**Action Servers** provide tools that modify system state by executing actions. These ser"
},
{
"path": "documents/docs/mcp/configuration.md",
"chars": 15048,
"preview": "# MCP Configuration Guide\n\n## Overview\n\nMCP configuration in UFO² uses a **hierarchical YAML structure** that maps agent"
},
{
"path": "documents/docs/mcp/data_collection.md",
"chars": 8779,
"preview": "# Data Collection Servers\n\n## Overview\n\n**Data Collection Servers** provide read-only tools that observe and retrieve sy"
},
{
"path": "documents/docs/mcp/local_servers.md",
"chars": 6002,
"preview": "# Local MCP Servers\n\nLocal MCP servers run in-process with the UFO² agent, providing fast and efficient access to tools "
},
{
"path": "documents/docs/mcp/overview.md",
"chars": 19360,
"preview": "# MCP (Model Context Protocol) - Overview\n\n## What is MCP?\n\n**MCP (Model Context Protocol)** is a standardized protocol "
}
]
// ... and 615 more files (download for full content)
About this extraction
This page contains the full source code of the microsoft/UFO GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 815 files (8.7 MB), approximately 2.3M tokens, and a symbol index with 7039 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.