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.
Files changed (284) hide show
  1. autobyteus/agent/agent.py +1 -1
  2. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
  3. autobyteus/agent/context/__init__.py +4 -2
  4. autobyteus/agent/context/agent_config.py +35 -8
  5. autobyteus/agent/context/agent_context_registry.py +73 -0
  6. autobyteus/agent/events/notifiers.py +4 -0
  7. autobyteus/agent/events/worker_event_dispatcher.py +1 -2
  8. autobyteus/agent/handlers/inter_agent_message_event_handler.py +8 -3
  9. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
  10. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
  11. autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
  12. autobyteus/agent/handlers/user_input_message_event_handler.py +16 -1
  13. autobyteus/agent/input_processor/__init__.py +1 -7
  14. autobyteus/agent/message/context_file_type.py +6 -0
  15. autobyteus/agent/message/send_message_to.py +74 -99
  16. autobyteus/agent/phases/discover.py +2 -1
  17. autobyteus/agent/runtime/agent_runtime.py +10 -2
  18. autobyteus/agent/runtime/agent_worker.py +1 -0
  19. autobyteus/agent/sender_type.py +15 -0
  20. autobyteus/agent/streaming/agent_event_stream.py +6 -0
  21. autobyteus/agent/streaming/stream_event_payloads.py +12 -0
  22. autobyteus/agent/streaming/stream_events.py +3 -0
  23. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
  24. autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
  25. autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
  26. autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
  27. autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
  28. autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
  29. autobyteus/agent/workspace/base_workspace.py +17 -2
  30. autobyteus/agent_team/__init__.py +1 -0
  31. autobyteus/agent_team/agent_team.py +93 -0
  32. autobyteus/agent_team/agent_team_builder.py +184 -0
  33. autobyteus/agent_team/base_agent_team.py +86 -0
  34. autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
  35. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
  36. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
  37. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
  38. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
  39. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
  40. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
  41. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
  42. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
  43. autobyteus/agent_team/context/__init__.py +17 -0
  44. autobyteus/agent_team/context/agent_team_config.py +33 -0
  45. autobyteus/agent_team/context/agent_team_context.py +61 -0
  46. autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
  47. autobyteus/agent_team/context/team_manager.py +147 -0
  48. autobyteus/agent_team/context/team_node_config.py +76 -0
  49. autobyteus/agent_team/events/__init__.py +29 -0
  50. autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
  51. autobyteus/agent_team/events/agent_team_events.py +53 -0
  52. autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
  53. autobyteus/agent_team/exceptions.py +8 -0
  54. autobyteus/agent_team/factory/__init__.py +9 -0
  55. autobyteus/agent_team/factory/agent_team_factory.py +99 -0
  56. autobyteus/agent_team/handlers/__init__.py +19 -0
  57. autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
  58. autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
  59. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
  60. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
  61. autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
  62. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
  63. autobyteus/agent_team/phases/__init__.py +11 -0
  64. autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
  65. autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
  66. autobyteus/agent_team/runtime/__init__.py +13 -0
  67. autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
  68. autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
  69. autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
  70. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
  71. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
  72. autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
  73. autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
  74. autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
  75. autobyteus/agent_team/streaming/__init__.py +26 -0
  76. autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
  77. autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
  78. autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
  79. autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
  80. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
  81. autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
  82. autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
  83. autobyteus/agent_team/task_notification/__init__.py +11 -0
  84. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
  85. autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
  86. autobyteus/agent_team/utils/__init__.py +9 -0
  87. autobyteus/agent_team/utils/wait_for_idle.py +46 -0
  88. autobyteus/cli/__init__.py +1 -1
  89. autobyteus/cli/agent_team_tui/__init__.py +4 -0
  90. autobyteus/cli/agent_team_tui/app.py +210 -0
  91. autobyteus/cli/agent_team_tui/state.py +180 -0
  92. autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
  93. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
  94. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
  95. autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
  96. autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
  97. autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
  98. autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
  99. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
  100. autobyteus/cli/cli_display.py +1 -1
  101. autobyteus/cli/workflow_tui/__init__.py +4 -0
  102. autobyteus/cli/workflow_tui/app.py +210 -0
  103. autobyteus/cli/workflow_tui/state.py +189 -0
  104. autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
  105. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
  106. autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
  107. autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
  108. autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
  109. autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
  110. autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
  111. autobyteus/events/event_types.py +8 -0
  112. autobyteus/llm/api/autobyteus_llm.py +11 -12
  113. autobyteus/llm/api/lmstudio_llm.py +34 -0
  114. autobyteus/llm/api/ollama_llm.py +8 -13
  115. autobyteus/llm/api/openai_compatible_llm.py +20 -3
  116. autobyteus/llm/autobyteus_provider.py +73 -46
  117. autobyteus/llm/llm_factory.py +103 -139
  118. autobyteus/llm/lmstudio_provider.py +104 -0
  119. autobyteus/llm/models.py +83 -53
  120. autobyteus/llm/ollama_provider.py +69 -61
  121. autobyteus/llm/ollama_provider_resolver.py +1 -0
  122. autobyteus/llm/providers.py +13 -12
  123. autobyteus/llm/runtimes.py +11 -0
  124. autobyteus/llm/token_counter/token_counter_factory.py +2 -0
  125. autobyteus/task_management/__init__.py +43 -0
  126. autobyteus/task_management/base_task_board.py +68 -0
  127. autobyteus/task_management/converters/__init__.py +11 -0
  128. autobyteus/task_management/converters/task_board_converter.py +64 -0
  129. autobyteus/task_management/converters/task_plan_converter.py +48 -0
  130. autobyteus/task_management/deliverable.py +16 -0
  131. autobyteus/task_management/deliverables/__init__.py +8 -0
  132. autobyteus/task_management/deliverables/file_deliverable.py +15 -0
  133. autobyteus/task_management/events.py +27 -0
  134. autobyteus/task_management/in_memory_task_board.py +126 -0
  135. autobyteus/task_management/schemas/__init__.py +15 -0
  136. autobyteus/task_management/schemas/deliverable_schema.py +13 -0
  137. autobyteus/task_management/schemas/plan_definition.py +35 -0
  138. autobyteus/task_management/schemas/task_status_report.py +27 -0
  139. autobyteus/task_management/task_plan.py +110 -0
  140. autobyteus/task_management/tools/__init__.py +14 -0
  141. autobyteus/task_management/tools/get_task_board_status.py +68 -0
  142. autobyteus/task_management/tools/publish_task_plan.py +113 -0
  143. autobyteus/task_management/tools/update_task_status.py +135 -0
  144. autobyteus/tools/__init__.py +2 -0
  145. autobyteus/tools/ask_user_input.py +2 -1
  146. autobyteus/tools/bash/bash_executor.py +61 -15
  147. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
  148. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
  149. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
  150. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
  151. autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
  152. autobyteus/tools/browser/standalone/navigate_to.py +2 -0
  153. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
  154. autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
  155. autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
  156. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
  157. autobyteus/tools/file/file_reader.py +36 -9
  158. autobyteus/tools/file/file_writer.py +37 -9
  159. autobyteus/tools/functional_tool.py +5 -4
  160. autobyteus/tools/image_downloader.py +2 -0
  161. autobyteus/tools/mcp/config_service.py +63 -58
  162. autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
  163. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
  164. autobyteus/tools/mcp/server_instance_manager.py +30 -4
  165. autobyteus/tools/mcp/tool_registrar.py +106 -51
  166. autobyteus/tools/parameter_schema.py +17 -11
  167. autobyteus/tools/pdf_downloader.py +2 -1
  168. autobyteus/tools/registry/tool_definition.py +36 -37
  169. autobyteus/tools/registry/tool_registry.py +50 -2
  170. autobyteus/tools/timer.py +2 -0
  171. autobyteus/tools/tool_category.py +15 -4
  172. autobyteus/tools/tool_meta.py +6 -1
  173. autobyteus/tools/tool_origin.py +10 -0
  174. autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
  175. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
  176. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
  177. autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
  178. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
  179. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
  180. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
  181. autobyteus/tools/usage/providers/__init__.py +2 -12
  182. autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
  183. autobyteus/tools/usage/registries/__init__.py +7 -12
  184. autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
  185. autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
  186. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
  187. autobyteus/workflow/agentic_workflow.py +93 -0
  188. autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
  189. autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
  190. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
  191. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
  192. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
  193. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
  194. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
  195. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
  196. autobyteus/workflow/context/__init__.py +17 -0
  197. autobyteus/workflow/context/team_manager.py +147 -0
  198. autobyteus/workflow/context/workflow_config.py +30 -0
  199. autobyteus/workflow/context/workflow_context.py +61 -0
  200. autobyteus/workflow/context/workflow_node_config.py +76 -0
  201. autobyteus/workflow/context/workflow_runtime_state.py +53 -0
  202. autobyteus/workflow/events/__init__.py +29 -0
  203. autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
  204. autobyteus/workflow/events/workflow_events.py +53 -0
  205. autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
  206. autobyteus/workflow/exceptions.py +8 -0
  207. autobyteus/workflow/factory/__init__.py +9 -0
  208. autobyteus/workflow/factory/workflow_factory.py +99 -0
  209. autobyteus/workflow/handlers/__init__.py +19 -0
  210. autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
  211. autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
  212. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
  213. autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
  214. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
  215. autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
  216. autobyteus/workflow/phases/__init__.py +11 -0
  217. autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
  218. autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
  219. autobyteus/workflow/runtime/__init__.py +13 -0
  220. autobyteus/workflow/runtime/workflow_runtime.py +82 -0
  221. autobyteus/workflow/runtime/workflow_worker.py +117 -0
  222. autobyteus/workflow/shutdown_steps/__init__.py +17 -0
  223. autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
  224. autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
  225. autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
  226. autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
  227. autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
  228. autobyteus/workflow/streaming/__init__.py +26 -0
  229. autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
  230. autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
  231. autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
  232. autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
  233. autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
  234. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
  235. autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
  236. autobyteus/workflow/utils/__init__.py +9 -0
  237. autobyteus/workflow/utils/wait_for_idle.py +46 -0
  238. autobyteus/workflow/workflow_builder.py +151 -0
  239. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/METADATA +16 -14
  240. autobyteus-1.1.5.dist-info/RECORD +455 -0
  241. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +1 -0
  242. examples/__init__.py +1 -0
  243. examples/agent_team/__init__.py +1 -0
  244. examples/discover_phase_transitions.py +104 -0
  245. examples/run_browser_agent.py +262 -0
  246. examples/run_google_slides_agent.py +287 -0
  247. examples/run_mcp_browser_client.py +174 -0
  248. examples/run_mcp_google_slides_client.py +270 -0
  249. examples/run_mcp_list_tools.py +189 -0
  250. examples/run_poem_writer.py +284 -0
  251. examples/run_sqlite_agent.py +295 -0
  252. autobyteus/agent/context/agent_phase_manager.py +0 -264
  253. autobyteus/agent/context/phases.py +0 -49
  254. autobyteus/agent/group/__init__.py +0 -0
  255. autobyteus/agent/group/agent_group.py +0 -164
  256. autobyteus/agent/group/agent_group_context.py +0 -81
  257. autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
  258. autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
  259. autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
  260. autobyteus/agent/workflow/__init__.py +0 -11
  261. autobyteus/agent/workflow/agentic_workflow.py +0 -89
  262. autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
  263. autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
  264. autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
  265. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
  266. autobyteus/tools/mcp/registrar.py +0 -202
  267. autobyteus/tools/usage/providers/json_example_provider.py +0 -32
  268. autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
  269. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
  270. autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
  271. autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
  272. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
  273. autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
  274. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
  275. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
  276. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
  277. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
  278. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
  279. autobyteus/workflow/simple_task.py +0 -98
  280. autobyteus/workflow/task.py +0 -147
  281. autobyteus/workflow/workflow.py +0 -49
  282. autobyteus-1.1.3.dist-info/RECORD +0 -312
  283. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
  284. {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,9 @@
1
+ # file: autobyteus/autobyteus/agent_team/utils/__init__.py
2
+ """
3
+ Utility functions for interacting with agent teams.
4
+ """
5
+ from .wait_for_idle import wait_for_team_to_be_idle
6
+
7
+ __all__ = [
8
+ "wait_for_team_to_be_idle",
9
+ ]
@@ -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()
@@ -6,6 +6,6 @@ from .agent_cli import run
6
6
  from .cli_display import InteractiveCLIDisplay
7
7
 
8
8
  __all__ = [
9
- "run",
9
+ "run",
10
10
  "InteractiveCLIDisplay",
11
11
  ]
@@ -0,0 +1,4 @@
1
+ # file: autobyteus/autobyteus/cli/agent_team_tui/__init__.py
2
+ """
3
+ A Textual-based TUI for interacting with Agent Teams.
4
+ """
@@ -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
+ )