autobyteus 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +1 -1
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
- autobyteus/agent/context/__init__.py +4 -2
- autobyteus/agent/context/agent_config.py +35 -8
- autobyteus/agent/context/agent_context_registry.py +73 -0
- autobyteus/agent/events/notifiers.py +4 -0
- autobyteus/agent/events/worker_event_dispatcher.py +1 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +8 -3
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
- autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
- autobyteus/agent/handlers/user_input_message_event_handler.py +16 -1
- autobyteus/agent/input_processor/__init__.py +1 -7
- autobyteus/agent/message/context_file_type.py +6 -0
- autobyteus/agent/message/send_message_to.py +74 -99
- autobyteus/agent/phases/discover.py +2 -1
- autobyteus/agent/runtime/agent_runtime.py +10 -2
- autobyteus/agent/runtime/agent_worker.py +1 -0
- autobyteus/agent/sender_type.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +6 -0
- autobyteus/agent/streaming/stream_event_payloads.py +12 -0
- autobyteus/agent/streaming/stream_events.py +3 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
- autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
- autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
- autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
- autobyteus/agent/workspace/base_workspace.py +17 -2
- autobyteus/agent_team/__init__.py +1 -0
- autobyteus/agent_team/agent_team.py +93 -0
- autobyteus/agent_team/agent_team_builder.py +184 -0
- autobyteus/agent_team/base_agent_team.py +86 -0
- autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
- autobyteus/agent_team/context/__init__.py +17 -0
- autobyteus/agent_team/context/agent_team_config.py +33 -0
- autobyteus/agent_team/context/agent_team_context.py +61 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
- autobyteus/agent_team/context/team_manager.py +147 -0
- autobyteus/agent_team/context/team_node_config.py +76 -0
- autobyteus/agent_team/events/__init__.py +29 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
- autobyteus/agent_team/events/agent_team_events.py +53 -0
- autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
- autobyteus/agent_team/exceptions.py +8 -0
- autobyteus/agent_team/factory/__init__.py +9 -0
- autobyteus/agent_team/factory/agent_team_factory.py +99 -0
- autobyteus/agent_team/handlers/__init__.py +19 -0
- autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
- autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
- autobyteus/agent_team/phases/__init__.py +11 -0
- autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
- autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
- autobyteus/agent_team/runtime/__init__.py +13 -0
- autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
- autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
- autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
- autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
- autobyteus/agent_team/streaming/__init__.py +26 -0
- autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
- autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
- autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
- autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
- autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
- autobyteus/agent_team/task_notification/__init__.py +11 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
- autobyteus/agent_team/utils/__init__.py +9 -0
- autobyteus/agent_team/utils/wait_for_idle.py +46 -0
- autobyteus/cli/__init__.py +1 -1
- autobyteus/cli/agent_team_tui/__init__.py +4 -0
- autobyteus/cli/agent_team_tui/app.py +210 -0
- autobyteus/cli/agent_team_tui/state.py +180 -0
- autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
- autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
- autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
- autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
- autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
- autobyteus/cli/cli_display.py +1 -1
- autobyteus/cli/workflow_tui/__init__.py +4 -0
- autobyteus/cli/workflow_tui/app.py +210 -0
- autobyteus/cli/workflow_tui/state.py +189 -0
- autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
- autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
- autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
- autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
- autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
- autobyteus/events/event_types.py +8 -0
- autobyteus/llm/api/autobyteus_llm.py +11 -12
- autobyteus/llm/api/lmstudio_llm.py +34 -0
- autobyteus/llm/api/ollama_llm.py +8 -13
- autobyteus/llm/api/openai_compatible_llm.py +20 -3
- autobyteus/llm/autobyteus_provider.py +73 -46
- autobyteus/llm/llm_factory.py +103 -139
- autobyteus/llm/lmstudio_provider.py +104 -0
- autobyteus/llm/models.py +83 -53
- autobyteus/llm/ollama_provider.py +69 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +13 -12
- autobyteus/llm/runtimes.py +11 -0
- autobyteus/llm/token_counter/token_counter_factory.py +2 -0
- autobyteus/task_management/__init__.py +43 -0
- autobyteus/task_management/base_task_board.py +68 -0
- autobyteus/task_management/converters/__init__.py +11 -0
- autobyteus/task_management/converters/task_board_converter.py +64 -0
- autobyteus/task_management/converters/task_plan_converter.py +48 -0
- autobyteus/task_management/deliverable.py +16 -0
- autobyteus/task_management/deliverables/__init__.py +8 -0
- autobyteus/task_management/deliverables/file_deliverable.py +15 -0
- autobyteus/task_management/events.py +27 -0
- autobyteus/task_management/in_memory_task_board.py +126 -0
- autobyteus/task_management/schemas/__init__.py +15 -0
- autobyteus/task_management/schemas/deliverable_schema.py +13 -0
- autobyteus/task_management/schemas/plan_definition.py +35 -0
- autobyteus/task_management/schemas/task_status_report.py +27 -0
- autobyteus/task_management/task_plan.py +110 -0
- autobyteus/task_management/tools/__init__.py +14 -0
- autobyteus/task_management/tools/get_task_board_status.py +68 -0
- autobyteus/task_management/tools/publish_task_plan.py +113 -0
- autobyteus/task_management/tools/update_task_status.py +135 -0
- autobyteus/tools/__init__.py +2 -0
- autobyteus/tools/ask_user_input.py +2 -1
- autobyteus/tools/bash/bash_executor.py +61 -15
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
- autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
- autobyteus/tools/browser/standalone/navigate_to.py +2 -0
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
- autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
- autobyteus/tools/file/file_reader.py +36 -9
- autobyteus/tools/file/file_writer.py +37 -9
- autobyteus/tools/functional_tool.py +5 -4
- autobyteus/tools/image_downloader.py +2 -0
- autobyteus/tools/mcp/config_service.py +63 -58
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server_instance_manager.py +30 -4
- autobyteus/tools/mcp/tool_registrar.py +106 -51
- autobyteus/tools/parameter_schema.py +17 -11
- autobyteus/tools/pdf_downloader.py +2 -1
- autobyteus/tools/registry/tool_definition.py +36 -37
- autobyteus/tools/registry/tool_registry.py +50 -2
- autobyteus/tools/timer.py +2 -0
- autobyteus/tools/tool_category.py +15 -4
- autobyteus/tools/tool_meta.py +6 -1
- autobyteus/tools/tool_origin.py +10 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
- autobyteus/tools/usage/providers/__init__.py +2 -12
- autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
- autobyteus/tools/usage/registries/__init__.py +7 -12
- autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
- autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
- autobyteus/workflow/agentic_workflow.py +93 -0
- autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
- autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
- autobyteus/workflow/context/__init__.py +17 -0
- autobyteus/workflow/context/team_manager.py +147 -0
- autobyteus/workflow/context/workflow_config.py +30 -0
- autobyteus/workflow/context/workflow_context.py +61 -0
- autobyteus/workflow/context/workflow_node_config.py +76 -0
- autobyteus/workflow/context/workflow_runtime_state.py +53 -0
- autobyteus/workflow/events/__init__.py +29 -0
- autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
- autobyteus/workflow/events/workflow_events.py +53 -0
- autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
- autobyteus/workflow/exceptions.py +8 -0
- autobyteus/workflow/factory/__init__.py +9 -0
- autobyteus/workflow/factory/workflow_factory.py +99 -0
- autobyteus/workflow/handlers/__init__.py +19 -0
- autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
- autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
- autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
- autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
- autobyteus/workflow/phases/__init__.py +11 -0
- autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
- autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
- autobyteus/workflow/runtime/__init__.py +13 -0
- autobyteus/workflow/runtime/workflow_runtime.py +82 -0
- autobyteus/workflow/runtime/workflow_worker.py +117 -0
- autobyteus/workflow/shutdown_steps/__init__.py +17 -0
- autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
- autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
- autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
- autobyteus/workflow/streaming/__init__.py +26 -0
- autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
- autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
- autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
- autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
- autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
- autobyteus/workflow/utils/__init__.py +9 -0
- autobyteus/workflow/utils/wait_for_idle.py +46 -0
- autobyteus/workflow/workflow_builder.py +151 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/METADATA +16 -14
- autobyteus-1.1.5.dist-info/RECORD +455 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +1 -0
- examples/__init__.py +1 -0
- examples/agent_team/__init__.py +1 -0
- examples/discover_phase_transitions.py +104 -0
- examples/run_browser_agent.py +262 -0
- examples/run_google_slides_agent.py +287 -0
- examples/run_mcp_browser_client.py +174 -0
- examples/run_mcp_google_slides_client.py +270 -0
- examples/run_mcp_list_tools.py +189 -0
- examples/run_poem_writer.py +284 -0
- examples/run_sqlite_agent.py +295 -0
- autobyteus/agent/context/agent_phase_manager.py +0 -264
- autobyteus/agent/context/phases.py +0 -49
- autobyteus/agent/group/__init__.py +0 -0
- autobyteus/agent/group/agent_group.py +0 -164
- autobyteus/agent/group/agent_group_context.py +0 -81
- autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
- autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
- autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
- autobyteus/agent/workflow/__init__.py +0 -11
- autobyteus/agent/workflow/agentic_workflow.py +0 -89
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
- autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
- autobyteus/tools/mcp/registrar.py +0 -202
- autobyteus/tools/usage/providers/json_example_provider.py +0 -32
- autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
- autobyteus/workflow/simple_task.py +0 -98
- autobyteus/workflow/task.py +0 -147
- autobyteus/workflow/workflow.py +0 -49
- autobyteus-1.1.3.dist-info/RECORD +0 -312
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/streaming/team_event_bridge.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent_team.streaming.agent_team_event_stream import AgentTeamEventStream
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
10
|
+
from autobyteus.agent_team.streaming.agent_team_event_notifier import AgentTeamExternalEventNotifier
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class TeamEventBridge:
|
|
15
|
+
"""
|
|
16
|
+
A dedicated component that bridges events from a sub-team's event stream
|
|
17
|
+
to the parent team's notifier.
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, sub_team: 'AgentTeam', sub_team_node_name: str, parent_notifier: 'AgentTeamExternalEventNotifier', loop: asyncio.AbstractEventLoop):
|
|
20
|
+
self._sub_team = sub_team
|
|
21
|
+
self._sub_team_node_name = sub_team_node_name
|
|
22
|
+
self._parent_notifier = parent_notifier
|
|
23
|
+
self._stream = AgentTeamEventStream(sub_team)
|
|
24
|
+
self._task: asyncio.Task = loop.create_task(self._run())
|
|
25
|
+
logger.info(f"TeamEventBridge created and task started for sub-team '{sub_team_node_name}'.")
|
|
26
|
+
|
|
27
|
+
async def _run(self):
|
|
28
|
+
"""The background task that consumes from the sub-team stream and re-publishes."""
|
|
29
|
+
try:
|
|
30
|
+
async for event in self._stream.all_events():
|
|
31
|
+
# Re-broadcast the event to the parent, adding the sub-team context.
|
|
32
|
+
self._parent_notifier.publish_sub_team_event(self._sub_team_node_name, event)
|
|
33
|
+
except asyncio.CancelledError:
|
|
34
|
+
logger.info(f"TeamEventBridge task for '{self._sub_team_node_name}' was cancelled.")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.error(f"Error in TeamEventBridge for '{self._sub_team_node_name}': {e}", exc_info=True)
|
|
37
|
+
finally:
|
|
38
|
+
logger.debug(f"TeamEventBridge task for '{self._sub_team_node_name}' is finishing.")
|
|
39
|
+
|
|
40
|
+
async def cancel(self):
|
|
41
|
+
"""Gracefully stops the bridge."""
|
|
42
|
+
logger.info(f"Cancelling TeamEventBridge for '{self._sub_team_node_name}'.")
|
|
43
|
+
if not self._task.done():
|
|
44
|
+
self._task.cancel()
|
|
45
|
+
try:
|
|
46
|
+
await self._task
|
|
47
|
+
except asyncio.CancelledError:
|
|
48
|
+
pass # Expected
|
|
49
|
+
await self._stream.close()
|
|
50
|
+
logger.info(f"TeamEventBridge for '{self._sub_team_node_name}' cancelled successfully.")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/task_notification/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains components for automatically notifying agents of runnable tasks.
|
|
4
|
+
"""
|
|
5
|
+
from .system_event_driven_agent_task_notifier import SystemEventDrivenAgentTaskNotifier
|
|
6
|
+
from .task_notification_mode import TaskNotificationMode
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"SystemEventDrivenAgentTaskNotifier",
|
|
10
|
+
"TaskNotificationMode",
|
|
11
|
+
]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Set, Any, TYPE_CHECKING, List, Union
|
|
5
|
+
|
|
6
|
+
from autobyteus.events.event_types import EventType
|
|
7
|
+
from autobyteus.agent_team.events import ProcessUserMessageEvent
|
|
8
|
+
from autobyteus.agent.message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.task_management.events import TaskPlanPublishedEvent, TaskStatusUpdatedEvent
|
|
10
|
+
from autobyteus.task_management.base_task_board import TaskStatus
|
|
11
|
+
from autobyteus.task_management.task_plan import Task
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from autobyteus.task_management.base_task_board import BaseTaskBoard
|
|
15
|
+
from autobyteus.agent_team.context.team_manager import TeamManager
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
class SystemEventDrivenAgentTaskNotifier:
|
|
20
|
+
"""
|
|
21
|
+
An internal component that monitors a TaskBoard and automatically sends
|
|
22
|
+
notifications to agents when their assigned tasks become runnable.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, task_board: 'BaseTaskBoard', team_manager: 'TeamManager'):
|
|
25
|
+
"""
|
|
26
|
+
Initializes the SystemEventDrivenAgentTaskNotifier.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
task_board: The team's shared task board instance.
|
|
30
|
+
team_manager: The team's manager for submitting notification events.
|
|
31
|
+
"""
|
|
32
|
+
if not task_board or not team_manager:
|
|
33
|
+
raise ValueError("TaskBoard and TeamManager are required for the notifier.")
|
|
34
|
+
|
|
35
|
+
self._task_board = task_board
|
|
36
|
+
self._team_manager = team_manager
|
|
37
|
+
self._dispatched_task_ids: Set[str] = set()
|
|
38
|
+
logger.info(f"SystemEventDrivenAgentTaskNotifier initialized for team '{self._team_manager.team_id}'.")
|
|
39
|
+
|
|
40
|
+
def start_monitoring(self):
|
|
41
|
+
"""
|
|
42
|
+
Subscribes to task board events to begin monitoring for runnable tasks.
|
|
43
|
+
This should be called once during the agent team's bootstrap process.
|
|
44
|
+
"""
|
|
45
|
+
self._task_board.subscribe(
|
|
46
|
+
EventType.TASK_BOARD_PLAN_PUBLISHED,
|
|
47
|
+
self._handle_task_board_update
|
|
48
|
+
)
|
|
49
|
+
self._task_board.subscribe(
|
|
50
|
+
EventType.TASK_BOARD_STATUS_UPDATED,
|
|
51
|
+
self._handle_task_board_update
|
|
52
|
+
)
|
|
53
|
+
logger.info(f"Team '{self._team_manager.team_id}': Task notifier is now monitoring TaskBoard events.")
|
|
54
|
+
|
|
55
|
+
async def _handle_task_board_update(self, payload: Union[TaskPlanPublishedEvent, TaskStatusUpdatedEvent], **kwargs):
|
|
56
|
+
"""
|
|
57
|
+
Asynchronous event handler triggered by the task board. It uses the event
|
|
58
|
+
payload to decide when to check for and notify agents of newly runnable tasks.
|
|
59
|
+
"""
|
|
60
|
+
if isinstance(payload, TaskPlanPublishedEvent):
|
|
61
|
+
logger.info(f"Team '{self._team_manager.team_id}': New task plan detected. Resetting dispatched tasks and checking for initial runnable tasks.")
|
|
62
|
+
self._dispatched_task_ids.clear()
|
|
63
|
+
await self._scan_and_notify_all_runnable_tasks()
|
|
64
|
+
|
|
65
|
+
elif isinstance(payload, TaskStatusUpdatedEvent):
|
|
66
|
+
# Only trigger a check for dependent tasks if a task has been completed,
|
|
67
|
+
# as this is the only status change that can unblock dependent tasks.
|
|
68
|
+
if payload.new_status == TaskStatus.COMPLETED:
|
|
69
|
+
logger.info(f"Team '{self._team_manager.team_id}': Task '{payload.task_id}' completed. Checking for newly unblocked dependent tasks.")
|
|
70
|
+
await self._check_and_notify_dependent_tasks(payload.task_id)
|
|
71
|
+
else:
|
|
72
|
+
logger.debug(f"Team '{self._team_manager.team_id}': Task '{payload.task_id}' status updated to '{payload.new_status.value}'. No dependent task check needed.")
|
|
73
|
+
else:
|
|
74
|
+
# This case should ideally not be hit with the new strong typing, but is kept as a safeguard.
|
|
75
|
+
logger.warning(f"Team '{self._team_manager.team_id}': Task notifier received an unhandled payload type: {type(payload)}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def _check_and_notify_dependent_tasks(self, completed_task_id: str):
|
|
79
|
+
"""
|
|
80
|
+
Finds tasks that depend on the completed task and notifies their assignees
|
|
81
|
+
if all of their other dependencies are also met.
|
|
82
|
+
"""
|
|
83
|
+
if not getattr(self._task_board, 'current_plan', None):
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
all_tasks = self._task_board.current_plan.tasks
|
|
87
|
+
task_statuses = getattr(self._task_board, 'task_statuses', {})
|
|
88
|
+
|
|
89
|
+
for child_task in all_tasks:
|
|
90
|
+
# Find tasks that are direct children of the completed task
|
|
91
|
+
if completed_task_id in child_task.dependencies:
|
|
92
|
+
# Now, check if this child task is fully runnable (all its parents are done)
|
|
93
|
+
all_deps_met = all(
|
|
94
|
+
task_statuses.get(dep_id) == TaskStatus.COMPLETED for dep_id in child_task.dependencies
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if all_deps_met and child_task.task_id not in self._dispatched_task_ids:
|
|
98
|
+
await self._dispatch_notification_for_task(child_task)
|
|
99
|
+
|
|
100
|
+
async def _scan_and_notify_all_runnable_tasks(self):
|
|
101
|
+
"""
|
|
102
|
+
Scans the entire board for any runnable tasks. Used for initial plan loading.
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
runnable_tasks = self._task_board.get_next_runnable_tasks()
|
|
106
|
+
for task in runnable_tasks:
|
|
107
|
+
if task.task_id not in self._dispatched_task_ids:
|
|
108
|
+
await self._dispatch_notification_for_task(task)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.error(f"Team '{self._team_manager.team_id}': Error during full scan for runnable tasks: {e}", exc_info=True)
|
|
111
|
+
|
|
112
|
+
async def _dispatch_notification_for_task(self, task: Task):
|
|
113
|
+
"""
|
|
114
|
+
Constructs and sends a context-rich notification for a single runnable task
|
|
115
|
+
by treating it as a user message to trigger the full processing pipeline.
|
|
116
|
+
It tags the message with metadata to indicate its system origin.
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
team_id = self._team_manager.team_id
|
|
120
|
+
logger.info(f"Team '{team_id}': Dispatching notification for runnable task '{task.task_name}' to assignee '{task.assignee_name}'.")
|
|
121
|
+
|
|
122
|
+
context_from_parents = []
|
|
123
|
+
if task.dependencies:
|
|
124
|
+
parent_task_deliverables_info = []
|
|
125
|
+
for dep_id in task.dependencies:
|
|
126
|
+
parent_task = getattr(self._task_board, '_task_map', {}).get(dep_id)
|
|
127
|
+
if parent_task and parent_task.file_deliverables:
|
|
128
|
+
deliverables_str = "\n".join(
|
|
129
|
+
[f" - File: {d.file_path}, Summary: {d.summary}" for d in parent_task.file_deliverables]
|
|
130
|
+
)
|
|
131
|
+
parent_task_deliverables_info.append(
|
|
132
|
+
f"The parent task '{parent_task.task_name}' produced the following deliverables:\n{deliverables_str}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if parent_task_deliverables_info:
|
|
136
|
+
context_from_parents.append(
|
|
137
|
+
"Your task is now unblocked. Here is the context from the completed parent task(s):\n" +
|
|
138
|
+
"\n\n".join(parent_task_deliverables_info)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
message_parts: List[str] = [f"Your task '{task.task_name}' is now ready to start."]
|
|
142
|
+
if context_from_parents:
|
|
143
|
+
message_parts.extend(context_from_parents)
|
|
144
|
+
|
|
145
|
+
message_parts.append(f"\nYour task description:\n{task.description}")
|
|
146
|
+
|
|
147
|
+
content = "\n\n".join(message_parts)
|
|
148
|
+
|
|
149
|
+
# Create the user message with metadata indicating its origin.
|
|
150
|
+
user_message = AgentInputUserMessage(
|
|
151
|
+
content=content,
|
|
152
|
+
metadata={'source': 'system_task_notifier'}
|
|
153
|
+
)
|
|
154
|
+
event = ProcessUserMessageEvent(
|
|
155
|
+
user_message=user_message,
|
|
156
|
+
target_agent_name=task.assignee_name
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Use the existing method for dispatching user messages.
|
|
160
|
+
await self._team_manager.dispatch_user_message_to_agent(event)
|
|
161
|
+
self._dispatched_task_ids.add(task.task_id)
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"Team '{self._team_manager.team_id}': Failed to dispatch notification for task '{task.task_id}': {e}", exc_info=True)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/task_notification/task_notification_mode.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the enum for controlling how task notifications are handled in an agent team.
|
|
4
|
+
"""
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
class TaskNotificationMode(str, Enum):
|
|
8
|
+
"""
|
|
9
|
+
Enumerates the modes for handling task notifications within an agent team.
|
|
10
|
+
"""
|
|
11
|
+
AGENT_MANUAL_NOTIFICATION = "agent_manual_notification"
|
|
12
|
+
"""
|
|
13
|
+
In this mode, an agent (typically the coordinator) is responsible for
|
|
14
|
+
manually sending notifications to other agents to start their tasks.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
SYSTEM_EVENT_DRIVEN = "system_event_driven"
|
|
18
|
+
"""
|
|
19
|
+
In this mode, the agent team framework automatically monitors the TaskBoard
|
|
20
|
+
and sends notifications to agents when their assigned tasks become runnable.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return self.value
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/utils/wait_for_idle.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent_team.streaming.agent_team_event_stream import AgentTeamEventStream
|
|
7
|
+
from autobyteus.agent_team.phases.agent_team_operational_phase import AgentTeamOperationalPhase
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
async def _wait_loop(streamer: AgentTeamEventStream, team_id: str):
|
|
15
|
+
"""Internal helper to listen for the IDLE or ERROR event."""
|
|
16
|
+
async for event in streamer.all_events():
|
|
17
|
+
if event.event_source_type == "TEAM" and event.data.new_phase == AgentTeamOperationalPhase.IDLE:
|
|
18
|
+
logger.info(f"Team '{team_id}' has become idle.")
|
|
19
|
+
return
|
|
20
|
+
if event.event_source_type == "TEAM" and event.data.new_phase == AgentTeamOperationalPhase.ERROR:
|
|
21
|
+
error_message = f"Team '{team_id}' entered an error state while waiting for idle: {event.data.error_message}"
|
|
22
|
+
logger.error(error_message)
|
|
23
|
+
raise RuntimeError(error_message)
|
|
24
|
+
|
|
25
|
+
async def wait_for_team_to_be_idle(team: 'AgentTeam', timeout: float = 60.0):
|
|
26
|
+
"""
|
|
27
|
+
Waits for an agent team to complete its bootstrapping and enter the IDLE state.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
team: The agent team instance to monitor.
|
|
31
|
+
timeout: The maximum time in seconds to wait.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
asyncio.TimeoutError: If the team does not become idle within the timeout period.
|
|
35
|
+
RuntimeError: If the team enters an error state.
|
|
36
|
+
"""
|
|
37
|
+
if team.get_current_phase() == AgentTeamOperationalPhase.IDLE:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
logger.info(f"Waiting for team '{team.team_id}' to become idle (timeout: {timeout}s)...")
|
|
41
|
+
|
|
42
|
+
streamer = AgentTeamEventStream(team)
|
|
43
|
+
try:
|
|
44
|
+
await asyncio.wait_for(_wait_loop(streamer, team.team_id), timeout=timeout)
|
|
45
|
+
finally:
|
|
46
|
+
await streamer.close()
|
autobyteus/cli/__init__.py
CHANGED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/cli/agent_team_tui/app.py
|
|
2
|
+
"""
|
|
3
|
+
The main Textual application class for the agent team TUI. This class orchestrates
|
|
4
|
+
the UI by reacting to changes in a central state store.
|
|
5
|
+
"""
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, Optional, Any
|
|
9
|
+
|
|
10
|
+
from textual.app import App, ComposeResult
|
|
11
|
+
from textual.containers import Horizontal
|
|
12
|
+
from textual.widgets import Header, Static
|
|
13
|
+
from textual.reactive import reactive
|
|
14
|
+
|
|
15
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
16
|
+
from autobyteus.agent_team.streaming.agent_team_event_stream import AgentTeamEventStream
|
|
17
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
18
|
+
from autobyteus.agent.streaming.stream_events import StreamEventType as AgentStreamEventType
|
|
19
|
+
from autobyteus.agent.streaming.stream_event_payloads import AssistantChunkData
|
|
20
|
+
from autobyteus.agent_team.streaming.agent_team_stream_event_payloads import AgentEventRebroadcastPayload, AgentTeamPhaseTransitionData
|
|
21
|
+
|
|
22
|
+
from .state import TUIStateStore
|
|
23
|
+
from .widgets.agent_list_sidebar import AgentListSidebar
|
|
24
|
+
from .widgets.focus_pane import FocusPane
|
|
25
|
+
from .widgets.status_bar import StatusBar
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
class AgentTeamApp(App):
|
|
30
|
+
"""A Textual TUI for interacting with an agent team, built around a central state store."""
|
|
31
|
+
|
|
32
|
+
TITLE = "AutoByteus"
|
|
33
|
+
CSS_PATH = "app.css"
|
|
34
|
+
BINDINGS = [
|
|
35
|
+
("d", "toggle_dark", "Toggle Dark Mode"),
|
|
36
|
+
("q", "quit", "Quit"),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
focused_node_data: reactive[Optional[Dict[str, Any]]] = reactive(None)
|
|
40
|
+
# The store_version property will trigger UI updates for the sidebar.
|
|
41
|
+
store_version: reactive[int] = reactive(0)
|
|
42
|
+
|
|
43
|
+
def __init__(self, team: AgentTeam, **kwargs):
|
|
44
|
+
super().__init__(**kwargs)
|
|
45
|
+
self.team = team
|
|
46
|
+
self.store = TUIStateStore(team=self.team)
|
|
47
|
+
self.team_stream: Optional[AgentTeamEventStream] = None
|
|
48
|
+
# Flag to indicate that the UI needs an update, used for throttling.
|
|
49
|
+
self._ui_update_pending = False
|
|
50
|
+
|
|
51
|
+
def compose(self) -> ComposeResult:
|
|
52
|
+
yield Header(id="app-header", name="AutoByteus Mission Control")
|
|
53
|
+
with Horizontal(id="main-container"):
|
|
54
|
+
yield AgentListSidebar(id="sidebar")
|
|
55
|
+
yield FocusPane(id="focus-pane")
|
|
56
|
+
yield StatusBar()
|
|
57
|
+
|
|
58
|
+
async def on_mount(self) -> None:
|
|
59
|
+
"""Start background tasks when the app is mounted."""
|
|
60
|
+
self.team.start()
|
|
61
|
+
self.team_stream = AgentTeamEventStream(self.team)
|
|
62
|
+
|
|
63
|
+
# Initialize the UI with the starting state
|
|
64
|
+
initial_tree = self.store.get_tree_data()
|
|
65
|
+
initial_focus_node = initial_tree.get(self.team.name)
|
|
66
|
+
|
|
67
|
+
self.store.set_focused_node(initial_focus_node)
|
|
68
|
+
self.focused_node_data = initial_focus_node
|
|
69
|
+
self.store_version = self.store.version # Trigger initial render
|
|
70
|
+
|
|
71
|
+
self.run_worker(self._listen_for_team_events(), name="team_listener")
|
|
72
|
+
|
|
73
|
+
# Set up a timer to run the throttled UI updater at ~15 FPS.
|
|
74
|
+
self.set_interval(1 / 15, self._throttled_ui_updater, name="ui_updater")
|
|
75
|
+
logger.info("Agent Team TUI mounted, team listener and throttled UI updater started.")
|
|
76
|
+
|
|
77
|
+
async def on_unmount(self) -> None:
|
|
78
|
+
if self.team and self.team.is_running:
|
|
79
|
+
await self.team.stop()
|
|
80
|
+
|
|
81
|
+
def _throttled_ui_updater(self) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Periodically checks if the UI state is dirty and, if so, triggers
|
|
84
|
+
reactive updates. It also flushes streaming buffers from the focus pane.
|
|
85
|
+
"""
|
|
86
|
+
focus_pane = self.query_one(FocusPane)
|
|
87
|
+
if self._ui_update_pending:
|
|
88
|
+
self._ui_update_pending = False
|
|
89
|
+
# This is the throttled trigger for the async watcher.
|
|
90
|
+
self.store_version = self.store.version
|
|
91
|
+
|
|
92
|
+
# Always flush the focus pane's streaming buffer for smooth text rendering.
|
|
93
|
+
focus_pane.flush_stream_buffers()
|
|
94
|
+
|
|
95
|
+
async def _listen_for_team_events(self) -> None:
|
|
96
|
+
"""A background worker that forwards team events to the state store and updates the UI."""
|
|
97
|
+
if not self.team_stream: return
|
|
98
|
+
try:
|
|
99
|
+
async for event in self.team_stream.all_events():
|
|
100
|
+
# 1. Always update the central state store immediately.
|
|
101
|
+
self.store.process_event(event)
|
|
102
|
+
|
|
103
|
+
# 2. Mark the UI as needing an update for the throttled components.
|
|
104
|
+
self._ui_update_pending = True
|
|
105
|
+
|
|
106
|
+
# 3. Handle real-time, incremental updates directly.
|
|
107
|
+
if isinstance(event.data, AgentEventRebroadcastPayload):
|
|
108
|
+
payload = event.data
|
|
109
|
+
agent_name = payload.agent_name
|
|
110
|
+
agent_event = payload.agent_event
|
|
111
|
+
focus_pane = self.query_one(FocusPane)
|
|
112
|
+
|
|
113
|
+
is_currently_focused = (focus_pane._focused_node_data and focus_pane._focused_node_data.get('name') == agent_name)
|
|
114
|
+
|
|
115
|
+
if is_currently_focused:
|
|
116
|
+
await focus_pane.add_agent_event(agent_event)
|
|
117
|
+
|
|
118
|
+
except asyncio.CancelledError:
|
|
119
|
+
logger.info("Agent team event listener task was cancelled.")
|
|
120
|
+
except Exception:
|
|
121
|
+
logger.error("Critical error in agent team TUI event listener", exc_info=True)
|
|
122
|
+
finally:
|
|
123
|
+
if self.team_stream: await self.team_stream.close()
|
|
124
|
+
|
|
125
|
+
# --- Reactive Watchers ---
|
|
126
|
+
|
|
127
|
+
async def watch_store_version(self, new_version: int):
|
|
128
|
+
"""
|
|
129
|
+
Reacts to changes in the store version.
|
|
130
|
+
"""
|
|
131
|
+
sidebar = self.query_one(AgentListSidebar)
|
|
132
|
+
focus_pane = self.query_one(FocusPane)
|
|
133
|
+
|
|
134
|
+
tree_data = self.store.get_tree_data()
|
|
135
|
+
agent_phases = self.store._agent_phases
|
|
136
|
+
team_phases = self.store._team_phases
|
|
137
|
+
speaking_agents = self.store._speaking_agents
|
|
138
|
+
|
|
139
|
+
sidebar.update_tree(tree_data, agent_phases, team_phases, speaking_agents)
|
|
140
|
+
|
|
141
|
+
focused_data = self.focused_node_data
|
|
142
|
+
if focused_data and focused_data.get("type") in ['team', 'subteam']:
|
|
143
|
+
node_name = focused_data['name']
|
|
144
|
+
task_plan = self.store.get_task_board_plan(node_name)
|
|
145
|
+
task_statuses = self.store.get_task_board_statuses(node_name)
|
|
146
|
+
await focus_pane.update_content(
|
|
147
|
+
node_data=focused_data,
|
|
148
|
+
history=[], # No history for teams
|
|
149
|
+
pending_approval=None,
|
|
150
|
+
all_agent_phases=agent_phases,
|
|
151
|
+
all_team_phases=team_phases,
|
|
152
|
+
task_plan=task_plan,
|
|
153
|
+
task_statuses=task_statuses
|
|
154
|
+
)
|
|
155
|
+
elif focused_data and focused_data.get("type") == 'agent':
|
|
156
|
+
focus_pane.update_current_node_status(agent_phases, team_phases)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def watch_focused_node_data(self, new_node_data: Optional[Dict[str, Any]]):
|
|
160
|
+
"""Reacts to changes in which node is focused. Primarily used for full pane reloads on user click."""
|
|
161
|
+
if not new_node_data: return
|
|
162
|
+
|
|
163
|
+
node_name = new_node_data['name']
|
|
164
|
+
node_type = new_node_data['type']
|
|
165
|
+
|
|
166
|
+
history = self.store.get_history_for_node(node_name, node_type)
|
|
167
|
+
pending_approval = self.store.get_pending_approval_for_agent(node_name) if node_type == 'agent' else None
|
|
168
|
+
|
|
169
|
+
task_plan = None
|
|
170
|
+
task_statuses = None
|
|
171
|
+
if node_type in ['team', 'subteam']:
|
|
172
|
+
task_plan = self.store.get_task_board_plan(node_name)
|
|
173
|
+
task_statuses = self.store.get_task_board_statuses(node_name)
|
|
174
|
+
|
|
175
|
+
sidebar = self.query_one(AgentListSidebar)
|
|
176
|
+
focus_pane = self.query_one(FocusPane)
|
|
177
|
+
|
|
178
|
+
await focus_pane.update_content(
|
|
179
|
+
node_data=new_node_data,
|
|
180
|
+
history=history,
|
|
181
|
+
pending_approval=pending_approval,
|
|
182
|
+
all_agent_phases=self.store._agent_phases,
|
|
183
|
+
all_team_phases=self.store._team_phases,
|
|
184
|
+
task_plan=task_plan,
|
|
185
|
+
task_statuses=task_statuses
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
sidebar.update_selection(node_name)
|
|
189
|
+
|
|
190
|
+
# --- Event Handlers (Actions) ---
|
|
191
|
+
|
|
192
|
+
def on_agent_list_sidebar_node_selected(self, message: AgentListSidebar.NodeSelected):
|
|
193
|
+
"""Handles a node being selected by updating the store and the app's reactive state."""
|
|
194
|
+
self.store.set_focused_node(message.node_data)
|
|
195
|
+
self.focused_node_data = message.node_data
|
|
196
|
+
|
|
197
|
+
async def on_focus_pane_message_submitted(self, message: FocusPane.MessageSubmitted):
|
|
198
|
+
"""Dispatches a user message to the backend model."""
|
|
199
|
+
user_message = AgentInputUserMessage(content=message.text)
|
|
200
|
+
await self.team.post_message(message=user_message, target_agent_name=message.agent_name)
|
|
201
|
+
|
|
202
|
+
async def on_focus_pane_approval_submitted(self, message: FocusPane.ApprovalSubmitted):
|
|
203
|
+
"""Dispatches a tool approval to the backend model."""
|
|
204
|
+
self.store.clear_pending_approval(message.agent_name)
|
|
205
|
+
await self.team.post_tool_execution_approval(
|
|
206
|
+
agent_name=message.agent_name,
|
|
207
|
+
tool_invocation_id=message.invocation_id,
|
|
208
|
+
is_approved=message.is_approved,
|
|
209
|
+
reason=message.reason,
|
|
210
|
+
)
|