autobyteus 1.1.4__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 (167) hide show
  1. autobyteus/agent/context/__init__.py +4 -2
  2. autobyteus/agent/context/agent_config.py +0 -4
  3. autobyteus/agent/context/agent_context_registry.py +73 -0
  4. autobyteus/agent/events/notifiers.py +4 -0
  5. autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
  6. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
  7. autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
  8. autobyteus/agent/message/send_message_to.py +29 -23
  9. autobyteus/agent/runtime/agent_runtime.py +10 -2
  10. autobyteus/agent/sender_type.py +15 -0
  11. autobyteus/agent/streaming/agent_event_stream.py +6 -0
  12. autobyteus/agent/streaming/stream_event_payloads.py +12 -0
  13. autobyteus/agent/streaming/stream_events.py +3 -0
  14. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
  15. autobyteus/agent_team/__init__.py +1 -0
  16. autobyteus/agent_team/agent_team.py +93 -0
  17. autobyteus/agent_team/agent_team_builder.py +184 -0
  18. autobyteus/agent_team/base_agent_team.py +86 -0
  19. autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
  20. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
  21. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
  22. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
  23. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
  24. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
  25. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
  26. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
  27. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
  28. autobyteus/agent_team/context/__init__.py +17 -0
  29. autobyteus/agent_team/context/agent_team_config.py +33 -0
  30. autobyteus/agent_team/context/agent_team_context.py +61 -0
  31. autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
  32. autobyteus/agent_team/context/team_manager.py +147 -0
  33. autobyteus/agent_team/context/team_node_config.py +76 -0
  34. autobyteus/agent_team/events/__init__.py +29 -0
  35. autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
  36. autobyteus/agent_team/events/agent_team_events.py +53 -0
  37. autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
  38. autobyteus/agent_team/exceptions.py +8 -0
  39. autobyteus/agent_team/factory/__init__.py +9 -0
  40. autobyteus/agent_team/factory/agent_team_factory.py +99 -0
  41. autobyteus/agent_team/handlers/__init__.py +19 -0
  42. autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
  43. autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
  44. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
  45. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
  46. autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
  47. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
  48. autobyteus/agent_team/phases/__init__.py +11 -0
  49. autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
  50. autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
  51. autobyteus/agent_team/runtime/__init__.py +13 -0
  52. autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
  53. autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
  54. autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
  55. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
  56. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
  57. autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
  58. autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
  59. autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
  60. autobyteus/agent_team/streaming/__init__.py +26 -0
  61. autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
  62. autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
  63. autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
  64. autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
  65. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
  66. autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
  67. autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
  68. autobyteus/agent_team/task_notification/__init__.py +11 -0
  69. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
  70. autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
  71. autobyteus/agent_team/utils/__init__.py +9 -0
  72. autobyteus/agent_team/utils/wait_for_idle.py +46 -0
  73. autobyteus/cli/agent_team_tui/__init__.py +4 -0
  74. autobyteus/cli/agent_team_tui/app.py +210 -0
  75. autobyteus/cli/agent_team_tui/state.py +180 -0
  76. autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
  77. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
  78. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
  79. autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
  80. autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
  81. autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
  82. autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
  83. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
  84. autobyteus/events/event_types.py +7 -2
  85. autobyteus/llm/api/autobyteus_llm.py +11 -12
  86. autobyteus/llm/api/lmstudio_llm.py +10 -13
  87. autobyteus/llm/api/ollama_llm.py +8 -13
  88. autobyteus/llm/autobyteus_provider.py +73 -46
  89. autobyteus/llm/llm_factory.py +102 -140
  90. autobyteus/llm/lmstudio_provider.py +63 -48
  91. autobyteus/llm/models.py +83 -53
  92. autobyteus/llm/ollama_provider.py +69 -61
  93. autobyteus/llm/ollama_provider_resolver.py +1 -0
  94. autobyteus/llm/providers.py +13 -13
  95. autobyteus/llm/runtimes.py +11 -0
  96. autobyteus/task_management/__init__.py +43 -0
  97. autobyteus/task_management/base_task_board.py +68 -0
  98. autobyteus/task_management/converters/__init__.py +11 -0
  99. autobyteus/task_management/converters/task_board_converter.py +64 -0
  100. autobyteus/task_management/converters/task_plan_converter.py +48 -0
  101. autobyteus/task_management/deliverable.py +16 -0
  102. autobyteus/task_management/deliverables/__init__.py +8 -0
  103. autobyteus/task_management/deliverables/file_deliverable.py +15 -0
  104. autobyteus/task_management/events.py +27 -0
  105. autobyteus/task_management/in_memory_task_board.py +126 -0
  106. autobyteus/task_management/schemas/__init__.py +15 -0
  107. autobyteus/task_management/schemas/deliverable_schema.py +13 -0
  108. autobyteus/task_management/schemas/plan_definition.py +35 -0
  109. autobyteus/task_management/schemas/task_status_report.py +27 -0
  110. autobyteus/task_management/task_plan.py +110 -0
  111. autobyteus/task_management/tools/__init__.py +14 -0
  112. autobyteus/task_management/tools/get_task_board_status.py +68 -0
  113. autobyteus/task_management/tools/publish_task_plan.py +113 -0
  114. autobyteus/task_management/tools/update_task_status.py +135 -0
  115. autobyteus/tools/bash/bash_executor.py +59 -14
  116. autobyteus/tools/mcp/config_service.py +63 -58
  117. autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
  118. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
  119. autobyteus/tools/mcp/server_instance_manager.py +30 -4
  120. autobyteus/tools/mcp/tool_registrar.py +103 -50
  121. autobyteus/tools/parameter_schema.py +17 -11
  122. autobyteus/tools/registry/tool_definition.py +24 -29
  123. autobyteus/tools/tool_category.py +1 -0
  124. autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
  125. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
  126. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
  127. autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
  128. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
  129. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
  130. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
  131. autobyteus/tools/usage/providers/__init__.py +2 -12
  132. autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
  133. autobyteus/tools/usage/registries/__init__.py +7 -12
  134. autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
  135. autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
  136. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
  137. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
  138. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
  139. examples/agent_team/__init__.py +1 -0
  140. examples/run_browser_agent.py +17 -15
  141. examples/run_google_slides_agent.py +17 -16
  142. examples/run_poem_writer.py +22 -12
  143. examples/run_sqlite_agent.py +17 -15
  144. autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
  145. autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
  146. autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
  147. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
  148. autobyteus/tools/usage/providers/json_example_provider.py +0 -32
  149. autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
  150. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
  151. autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
  152. autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
  153. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
  154. autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
  155. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
  156. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
  157. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
  158. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
  159. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
  160. examples/workflow/__init__.py +0 -1
  161. examples/workflow/run_basic_research_workflow.py +0 -189
  162. examples/workflow/run_code_review_workflow.py +0 -269
  163. examples/workflow/run_debate_workflow.py +0 -212
  164. examples/workflow/run_workflow_with_tui.py +0 -153
  165. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
  166. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
  167. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +0 -0
@@ -4,10 +4,12 @@ Components related to the agent's runtime context, state, config, and status man
4
4
  """
5
5
  from .agent_config import AgentConfig
6
6
  from .agent_runtime_state import AgentRuntimeState
7
- from .agent_context import AgentContext
7
+ from .agent_context import AgentContext
8
+ from .agent_context_registry import AgentContextRegistry
8
9
 
9
10
  __all__ = [
10
11
  "AgentContext",
11
12
  "AgentConfig",
12
- "AgentRuntimeState",
13
+ "AgentRuntimeState",
14
+ "AgentContextRegistry",
13
15
  ]
@@ -37,7 +37,6 @@ class AgentConfig:
37
37
  system_prompt: Optional[str] = None,
38
38
  tools: Optional[List['BaseTool']] = None,
39
39
  auto_execute_tools: bool = True,
40
- use_xml_tool_format: bool = True,
41
40
  input_processors: Optional[List['BaseAgentUserInputMessageProcessor']] = None,
42
41
  llm_response_processors: Optional[List['BaseLLMResponseProcessor']] = None,
43
42
  system_prompt_processors: Optional[List['BaseSystemPromptProcessor']] = None,
@@ -58,7 +57,6 @@ class AgentConfig:
58
57
  llm_instance's config will be used as the base.
59
58
  tools: An optional list of pre-initialized tool instances (subclasses of BaseTool).
60
59
  auto_execute_tools: If True, the agent will execute tools without approval.
61
- use_xml_tool_format: Whether to use XML for tool descriptions and examples.
62
60
  input_processors: A list of input processor instances.
63
61
  llm_response_processors: A list of LLM response processor instances.
64
62
  system_prompt_processors: A list of system prompt processor instances.
@@ -76,7 +74,6 @@ class AgentConfig:
76
74
  self.tools = tools or []
77
75
  self.workspace = workspace
78
76
  self.auto_execute_tools = auto_execute_tools
79
- self.use_xml_tool_format = use_xml_tool_format
80
77
  self.input_processors = input_processors or []
81
78
  self.llm_response_processors = llm_response_processors if llm_response_processors is not None else list(self.DEFAULT_LLM_RESPONSE_PROCESSORS)
82
79
  self.system_prompt_processors = system_prompt_processors if system_prompt_processors is not None else list(self.DEFAULT_SYSTEM_PROMPT_PROCESSORS)
@@ -101,7 +98,6 @@ class AgentConfig:
101
98
  system_prompt=self.system_prompt,
102
99
  tools=self.tools.copy(), # Shallow copy the list, but reference the original tool instances
103
100
  auto_execute_tools=self.auto_execute_tools,
104
- use_xml_tool_format=self.use_xml_tool_format,
105
101
  input_processors=self.input_processors.copy(), # Shallow copy the list
106
102
  llm_response_processors=self.llm_response_processors.copy(), # Shallow copy the list
107
103
  system_prompt_processors=self.system_prompt_processors.copy(), # Shallow copy the list
@@ -0,0 +1,73 @@
1
+ # file: autobyteus/autobyteus/agent/context/agent_context_registry.py
2
+ import logging
3
+ from typing import Dict, Optional, TYPE_CHECKING
4
+ import weakref
5
+
6
+ from autobyteus.utils.singleton import SingletonMeta
7
+
8
+ if TYPE_CHECKING:
9
+ from .agent_context import AgentContext
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class AgentContextRegistry(metaclass=SingletonMeta):
14
+ """
15
+ A singleton registry that holds weak references to active AgentContext objects,
16
+ keyed by their agent_id.
17
+
18
+ This allows other services to look up an agent's context without creating
19
+ tight coupling or preventing garbage collection.
20
+ """
21
+ def __init__(self):
22
+ self._contexts: Dict[str, weakref.ReferenceType['AgentContext']] = {}
23
+ logger.info("AgentContextRegistry (Singleton) initialized.")
24
+
25
+ def register_context(self, context: 'AgentContext'):
26
+ """
27
+ Registers an agent's context. A weak reference is stored to prevent
28
+ circular references and allow for proper garbage collection.
29
+
30
+ Args:
31
+ context: The AgentContext instance to register.
32
+ """
33
+ agent_id = context.agent_id
34
+ if agent_id in self._contexts and self._contexts[agent_id]() is not None:
35
+ logger.warning(f"AgentContext for agent_id '{agent_id}' is already registered. Overwriting.")
36
+
37
+ self._contexts[agent_id] = weakref.ref(context)
38
+ logger.info(f"Registered AgentContext for agent_id '{agent_id}'. Total registered contexts: {len(self._contexts)}")
39
+
40
+ def unregister_context(self, agent_id: str):
41
+ """
42
+ Unregisters an agent's context, typically on agent shutdown.
43
+
44
+ Args:
45
+ agent_id: The ID of the agent whose context should be removed.
46
+ """
47
+ if agent_id in self._contexts:
48
+ del self._contexts[agent_id]
49
+ logger.info(f"Unregistered AgentContext for agent_id '{agent_id}'.")
50
+ else:
51
+ logger.warning(f"Attempted to unregister a non-existent AgentContext for agent_id '{agent_id}'.")
52
+
53
+ def get_context(self, agent_id: str) -> Optional['AgentContext']:
54
+ """
55
+ Retrieves an active agent's context by its ID.
56
+
57
+ Args:
58
+ agent_id: The ID of the agent.
59
+
60
+ Returns:
61
+ The AgentContext instance if found and still alive, otherwise None.
62
+ """
63
+ context_ref = self._contexts.get(agent_id)
64
+ if context_ref:
65
+ context = context_ref()
66
+ if context:
67
+ return context
68
+ else:
69
+ # The weak reference is dead, so we can clean it up.
70
+ logger.debug(f"Cleaning up dead weak reference for agent_id '{agent_id}'.")
71
+ del self._contexts[agent_id]
72
+
73
+ return None
@@ -112,6 +112,10 @@ class AgentExternalEventNotifier(EventEmitter):
112
112
  def notify_agent_tool_invocation_auto_executing(self, auto_exec_data: Dict[str, Any]):
113
113
  """Notifies that a tool is being automatically executed."""
114
114
  self._emit_event(EventType.AGENT_TOOL_INVOCATION_AUTO_EXECUTING, payload_content=auto_exec_data)
115
+
116
+ def notify_agent_data_system_task_notification_received(self, notification_data: Dict[str, Any]):
117
+ """Notifies that the agent has received a system-generated task notification."""
118
+ self._emit_event(EventType.AGENT_DATA_SYSTEM_TASK_NOTIFICATION_RECEIVED, payload_content=notification_data)
115
119
 
116
120
  def notify_agent_error_output_generation(self, error_source: str, error_message: str, error_details: Optional[str] = None):
117
121
  payload_dict = {
@@ -6,9 +6,11 @@ from autobyteus.agent.handlers.base_event_handler import AgentEventHandler
6
6
  from autobyteus.agent.events import InterAgentMessageReceivedEvent, LLMUserMessageReadyEvent
7
7
  from autobyteus.agent.message.inter_agent_message import InterAgentMessage
8
8
  from autobyteus.llm.user_message import LLMUserMessage
9
+ from autobyteus.agent.sender_type import TASK_NOTIFIER_SENDER_ID # New import
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from autobyteus.agent.context import AgentContext
13
+ from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
12
14
 
13
15
  logger = logging.getLogger(__name__)
14
16
 
@@ -46,6 +48,10 @@ class InterAgentMessageReceivedEventHandler(AgentEventHandler):
46
48
  f"Content: '{inter_agent_msg.content}'"
47
49
  )
48
50
 
51
+ # This handler now only deals with messages from other agents, not the system notifier.
52
+ # The logic for system task notifications has been moved to UserInputMessageEventHandler
53
+ # by checking the message metadata.
54
+
49
55
  content_for_llm = (
50
56
  f"You have received a message from another agent.\n"
51
57
  f"Sender Agent ID: {inter_agent_msg.sender_agent_id}\n"
@@ -54,7 +60,7 @@ class InterAgentMessageReceivedEventHandler(AgentEventHandler):
54
60
  f"--- Message Content ---\n"
55
61
  f"{inter_agent_msg.content}\n"
56
62
  f"--- End of Message Content ---\n"
57
- f"Please process this information and respond or act accordingly."
63
+ f"Please process this information and act accordingly."
58
64
  )
59
65
 
60
66
  context.state.add_message_to_history({
@@ -67,7 +73,6 @@ class InterAgentMessageReceivedEventHandler(AgentEventHandler):
67
73
  llm_user_message = LLMUserMessage(content=content_for_llm)
68
74
 
69
75
  llm_user_message_ready_event = LLMUserMessageReadyEvent(llm_user_message=llm_user_message)
70
- # MODIFIED: Use context.input_event_queues and not context.state.queues
71
76
  await context.input_event_queues.enqueue_internal_system_event(llm_user_message_ready_event)
72
77
 
73
78
  logger.info(
@@ -86,10 +86,8 @@ class LLMCompleteResponseReceivedEventHandler(AgentEventHandler):
86
86
  any_processor_took_action = True
87
87
  logger.info(
88
88
  f"Agent '{agent_id}': LLMResponseProcessor '{processor_name_for_log}' "
89
- f"handled the response. This LLM response "
90
- f"will not be emitted as a complete response by this handler instance."
89
+ f"handled the response."
91
90
  )
92
- break
93
91
  else:
94
92
  logger.debug(f"Agent '{agent_id}': LLMResponseProcessor '{processor_name_for_log}' did not handle the response.")
95
93
 
@@ -102,8 +100,7 @@ class LLMCompleteResponseReceivedEventHandler(AgentEventHandler):
102
100
  error_message="The model's response contained a malformed tool call that could not be understood.",
103
101
  error_details=str(e_parse)
104
102
  )
105
- any_processor_took_action = True # Mark as handled to prevent printing raw response
106
- break
103
+ # A parsing failure should not prevent other processors from running.
107
104
 
108
105
  except Exception as e: # pragma: no cover
109
106
  logger.error(f"Agent '{agent_id}': Error while using LLMResponseProcessor '{processor_name_for_log}': {e}. This processor is skipped.", exc_info=True)
@@ -118,21 +115,24 @@ class LLMCompleteResponseReceivedEventHandler(AgentEventHandler):
118
115
  f"Skipping LLMResponseProcessor attempts."
119
116
  )
120
117
 
121
- if not any_processor_took_action:
122
- if is_error_response:
123
- logger.info(
124
- f"Agent '{agent_id}' emitting a received error message as a complete response: '{complete_response_text[:100]}...'"
118
+ # Always notify that the LLM's response is complete, regardless of processor actions.
119
+ # This serves as a critical signal to the frontend that the stream for this turn has ended.
120
+ if notifier:
121
+ if any_processor_took_action:
122
+ log_message = (
123
+ f"Agent '{agent_id}': One or more LLMResponseProcessors handled the response. "
124
+ f"Now emitting AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE as a completion signal."
125
125
  )
126
126
  else:
127
- logger.info(
127
+ log_message = (
128
128
  f"Agent '{agent_id}': No LLMResponseProcessor handled the response. "
129
- f"Emitting the current LLM response as a complete response for this leg."
129
+ f"Emitting the full LLM response as a final answer and completion signal."
130
130
  )
131
-
132
- if notifier:
133
- try:
134
- # The complete_response object now contains both content and reasoning
135
- notifier.notify_agent_data_assistant_complete_response(complete_response)
136
- logger.debug(f"Agent '{agent_id}' emitted AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE event.")
137
- except Exception as e_notify: # pragma: no cover
138
- logger.error(f"Agent '{agent_id}': Error emitting AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE: {e_notify}", exc_info=True)
131
+ logger.info(log_message)
132
+
133
+ try:
134
+ # The complete_response object now contains both content and reasoning
135
+ notifier.notify_agent_data_assistant_complete_response(complete_response)
136
+ logger.debug(f"Agent '{agent_id}' emitted AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE event successfully.")
137
+ except Exception as e_notify: # pragma: no cover
138
+ logger.error(f"Agent '{agent_id}': Error emitting AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE: {e_notify}", exc_info=True)
@@ -11,6 +11,7 @@ from autobyteus.llm.user_message import LLMUserMessage
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from autobyteus.agent.context import AgentContext
14
+ from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -20,6 +21,7 @@ class UserInputMessageEventHandler(AgentEventHandler):
20
21
  AgentUserInputMessageProcessors (provided as instances) to the AgentInputUserMessage,
21
22
  then converting the processed message into an LLMUserMessage, and finally
22
23
  enqueuing an LLMUserMessageReadyEvent for further processing by the LLM.
24
+ It also checks for metadata to emit special notifications for system-generated tasks.
23
25
  """
24
26
 
25
27
  def __init__(self):
@@ -33,6 +35,19 @@ class UserInputMessageEventHandler(AgentEventHandler):
33
35
  return
34
36
 
35
37
  original_agent_input_user_msg: AgentInputUserMessage = event.agent_input_user_message
38
+
39
+ # --- NEW LOGIC: Check metadata for system-generated tasks and notify TUI ---
40
+ if original_agent_input_user_msg.metadata.get('source') == 'system_task_notifier':
41
+ if context.phase_manager:
42
+ notifier: 'AgentExternalEventNotifier' = context.phase_manager.notifier
43
+ notification_data = {
44
+ "sender_id": "system.task_notifier",
45
+ "content": original_agent_input_user_msg.content,
46
+ }
47
+ notifier.notify_agent_data_system_task_notification_received(notification_data)
48
+ logger.info(f"Agent '{context.agent_id}' emitted system task notification for TUI.")
49
+ # --- END NEW LOGIC ---
50
+
36
51
  processed_agent_input_user_msg: AgentInputUserMessage = original_agent_input_user_msg
37
52
 
38
53
  logger.info(f"Agent '{context.agent_id}' handling UserMessageReceivedEvent: '{original_agent_input_user_msg.content}'")
@@ -1,4 +1,4 @@
1
- # file: autobyteus/autobyteus/agent/message/send_message_to.py
1
+ # file: autobyteus/agent/message/send_message_to.py
2
2
  import logging
3
3
  from typing import TYPE_CHECKING, Any, Optional
4
4
 
@@ -9,33 +9,30 @@ from autobyteus.tools.tool_config import ToolConfig
9
9
  # This import is for type hinting only and avoids circular dependencies at runtime
10
10
  if TYPE_CHECKING:
11
11
  from autobyteus.agent.context import AgentContext
12
- from autobyteus.workflow.context.team_manager import TeamManager
13
- from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
12
+ from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
13
+ from autobyteus.agent_team.context.team_manager import TeamManager
14
+ from autobyteus.agent_team.events.agent_team_events import InterAgentMessageRequestEvent
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
  class SendMessageTo(BaseTool):
18
19
  """
19
- A tool for sending messages to other agents within the same workflow team.
20
- This tool requires a TeamManager to be injected at runtime by the
21
- workflow framework to enable communication with the parent orchestrator.
20
+ A tool for sending messages to other agents within the same agent team.
21
+ This tool dynamically retrieves the team communication channel from the
22
+ agent's context at runtime.
22
23
  """
23
24
  TOOL_NAME = "SendMessageTo"
24
25
  CATEGORY = ToolCategory.AGENT_COMMUNICATION
25
26
 
26
27
  def __init__(self, config: Optional[ToolConfig] = None):
27
28
  """
28
- Initializes the SendMessageTo tool. The TeamManager is injected separately
29
- after instantiation.
29
+ Initializes the stateless SendMessageTo tool.
30
30
  """
31
31
  super().__init__(config=config)
32
- self._team_manager: Optional['TeamManager'] = None
33
- logger.debug("SendMessageTo tool initialized. TeamManager is not yet injected.")
32
+ # The TeamManager is no longer stored as an instance variable.
33
+ logger.debug("SendMessageTo tool initialized (stateless).")
34
34
 
35
- def set_team_manager(self, team_manager: 'TeamManager'):
36
- """Sets the TeamManager instance after the tool has been created."""
37
- self._team_manager = team_manager
38
- logger.debug(f"TeamManager was set on SendMessageTo instance post-creation.")
35
+ # The set_team_manager method has been removed.
39
36
 
40
37
  @classmethod
41
38
  def get_name(cls) -> str:
@@ -75,14 +72,23 @@ class SendMessageTo(BaseTool):
75
72
  content: str,
76
73
  message_type: str) -> str:
77
74
  """
78
- Creates and dispatches a InterAgentMessageRequestEvent to the parent workflow
79
- using the injected team_manager.
75
+ Creates and dispatches an InterAgentMessageRequestEvent to the parent agent team
76
+ by retrieving the TeamManager from the agent's context.
80
77
  """
81
78
  # Local import to break circular dependency at module load time.
82
- from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
79
+ from autobyteus.agent_team.events.agent_team_events import InterAgentMessageRequestEvent
83
80
 
84
- if self._team_manager is None:
85
- error_msg = "Critical error: SendMessageTo tool is not configured for workflow communication. It can only be used within a managed AgenticWorkflow."
81
+ # --- NEW: Retrieve TeamManager dynamically from context ---
82
+ team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
83
+ if not team_context:
84
+ error_msg = "Critical error: SendMessageTo tool is not configured for team communication. It can only be used within a managed AgentTeam."
85
+ logger.error(f"Agent '{context.agent_id}': {error_msg}")
86
+ return f"Error: {error_msg}"
87
+
88
+ team_manager: Optional['TeamManager'] = team_context.team_manager
89
+ if not team_manager:
90
+ # This is an internal framework error and should not happen in a correctly configured team.
91
+ error_msg = "Internal Error: TeamManager not found in the provided team_context."
86
92
  logger.error(f"Agent '{context.agent_id}': {error_msg}")
87
93
  return f"Error: {error_msg}"
88
94
 
@@ -103,7 +109,7 @@ class SendMessageTo(BaseTool):
103
109
  sender_agent_id = context.agent_id
104
110
  logger.info(f"Tool '{self.get_name()}': Agent '{sender_agent_id}' requesting to send message to '{recipient_name}'.")
105
111
 
106
- # Create the event for the workflow to handle
112
+ # Create the event for the agent team to handle
107
113
  event = InterAgentMessageRequestEvent(
108
114
  sender_agent_id=sender_agent_id,
109
115
  recipient_name=recipient_name,
@@ -111,9 +117,9 @@ class SendMessageTo(BaseTool):
111
117
  message_type=message_type
112
118
  )
113
119
 
114
- # Dispatch the event "up" to the workflow's event loop via the team manager
115
- await self._team_manager.dispatch_inter_agent_message_request(event)
120
+ # Dispatch the event "up" to the team's event loop via the dynamically retrieved team manager
121
+ await team_manager.dispatch_inter_agent_message_request(event)
116
122
 
117
123
  success_msg = f"Message dispatch for recipient '{recipient_name}' has been successfully requested."
118
124
  logger.info(f"Tool '{self.get_name()}': {success_msg}")
119
- return success_msg
125
+ return success_msg
@@ -5,7 +5,7 @@ import traceback
5
5
  import concurrent.futures
6
6
  from typing import Optional, Any, Callable, Awaitable, TYPE_CHECKING
7
7
 
8
- from autobyteus.agent.context import AgentContext
8
+ from autobyteus.agent.context import AgentContext, AgentContextRegistry
9
9
  from autobyteus.agent.phases import AgentOperationalPhase, AgentPhaseManager
10
10
  from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
11
11
  from autobyteus.agent.events import BaseEvent
@@ -39,8 +39,12 @@ class AgentRuntime:
39
39
  event_handler_registry=self.event_handler_registry,
40
40
  )
41
41
  self._worker.add_done_callback(self._handle_worker_completion)
42
+
43
+ # Register the context with the global registry
44
+ self._context_registry = AgentContextRegistry()
45
+ self._context_registry.register_context(self.context)
42
46
 
43
- logger.info(f"AgentRuntime initialized for agent_id '{self.context.agent_id}'.")
47
+ logger.info(f"AgentRuntime initialized for agent_id '{self.context.agent_id}'. Context registered.")
44
48
 
45
49
  def get_worker_loop(self) -> Optional[asyncio.AbstractEventLoop]:
46
50
  return self._worker.get_worker_loop()
@@ -122,6 +126,10 @@ class AgentRuntime:
122
126
 
123
127
  # LLM instance cleanup is now handled by the AgentWorker before its loop closes.
124
128
 
129
+ # Unregister the context from the global registry
130
+ self._context_registry.unregister_context(agent_id)
131
+ logger.info(f"AgentRuntime for '{agent_id}': Context unregistered.")
132
+
125
133
  await self.phase_manager.notify_final_shutdown_complete()
126
134
  logger.info(f"AgentRuntime for '{agent_id}' stop() method completed.")
127
135
 
@@ -0,0 +1,15 @@
1
+ # file: autobyteus/autobyteus/agent/sender_type.py
2
+ from enum import Enum
3
+ from typing import Set
4
+
5
+ class SenderType(str, Enum):
6
+ """
7
+ Categorizes the origin of a message or event within the system.
8
+ """
9
+ USER = "user" # A message originating from an external human user.
10
+ AGENT = "agent" # A message from another agent within the same team or a different team.
11
+ SYSTEM = "system" # An automated message from an internal system component.
12
+
13
+
14
+ # --- System Sender Identification ---
15
+ TASK_NOTIFIER_SENDER_ID = "system.task_notifier"
@@ -16,6 +16,7 @@ from autobyteus.agent.streaming.stream_event_payloads import (
16
16
  create_error_event_data,
17
17
  create_tool_invocation_approval_requested_data,
18
18
  create_tool_invocation_auto_executing_data,
19
+ create_system_task_notification_data, # NEW
19
20
  AssistantChunkData,
20
21
  AssistantCompleteResponseData,
21
22
  ToolInteractionLogEntryData,
@@ -23,6 +24,7 @@ from autobyteus.agent.streaming.stream_event_payloads import (
23
24
  ToolInvocationApprovalRequestedData,
24
25
  ToolInvocationAutoExecutingData,
25
26
  ErrorEventData,
27
+ SystemTaskNotificationData, # NEW
26
28
  EmptyData,
27
29
  StreamDataPayload,
28
30
  )
@@ -101,6 +103,10 @@ class AgentEventStream(EventEmitter):
101
103
  elif event_type == EventType.AGENT_ERROR_OUTPUT_GENERATION:
102
104
  typed_payload_for_stream_event = create_error_event_data(payload)
103
105
  stream_event_type_for_generic_stream = StreamEventType.ERROR_EVENT
106
+ # NEW MAPPING
107
+ elif event_type == EventType.AGENT_DATA_SYSTEM_TASK_NOTIFICATION_RECEIVED:
108
+ typed_payload_for_stream_event = create_system_task_notification_data(payload)
109
+ stream_event_type_for_generic_stream = StreamEventType.SYSTEM_TASK_NOTIFICATION
104
110
 
105
111
  elif event_type in [EventType.AGENT_DATA_ASSISTANT_CHUNK_STREAM_END, EventType.AGENT_DATA_TOOL_LOG_STREAM_END]:
106
112
  pass
@@ -55,6 +55,11 @@ class ToolInvocationAutoExecutingData(BaseStreamPayload):
55
55
  tool_name: str
56
56
  arguments: Dict[str, Any]
57
57
 
58
+ # NEW PAYLOAD
59
+ class SystemTaskNotificationData(BaseStreamPayload):
60
+ sender_id: str
61
+ content: str
62
+
58
63
  class EmptyData(BaseStreamPayload):
59
64
  pass
60
65
 
@@ -67,6 +72,7 @@ StreamDataPayload = Union[
67
72
  ErrorEventData,
68
73
  ToolInvocationApprovalRequestedData,
69
74
  ToolInvocationAutoExecutingData,
75
+ SystemTaskNotificationData, # NEW
70
76
  EmptyData
71
77
  ]
72
78
 
@@ -165,3 +171,9 @@ def create_tool_invocation_auto_executing_data(auto_exec_data_dict: Any) -> Tool
165
171
  if isinstance(auto_exec_data_dict, dict):
166
172
  return ToolInvocationAutoExecutingData(**auto_exec_data_dict)
167
173
  raise ValueError(f"Cannot create ToolInvocationAutoExecutingData from {type(auto_exec_data_dict)}")
174
+
175
+ # NEW FACTORY FUNCTION
176
+ def create_system_task_notification_data(notification_data_dict: Any) -> SystemTaskNotificationData:
177
+ if isinstance(notification_data_dict, dict):
178
+ return SystemTaskNotificationData(**notification_data_dict)
179
+ raise ValueError(f"Cannot create SystemTaskNotificationData from {type(notification_data_dict)}")
@@ -16,6 +16,7 @@ from .stream_event_payloads import (
16
16
  ErrorEventData,
17
17
  ToolInvocationApprovalRequestedData,
18
18
  ToolInvocationAutoExecutingData,
19
+ SystemTaskNotificationData, # NEW
19
20
  EmptyData
20
21
  )
21
22
 
@@ -33,6 +34,7 @@ class StreamEventType(str, Enum):
33
34
  ERROR_EVENT = "error_event"
34
35
  TOOL_INVOCATION_APPROVAL_REQUESTED = "tool_invocation_approval_requested"
35
36
  TOOL_INVOCATION_AUTO_EXECUTING = "tool_invocation_auto_executing"
37
+ SYSTEM_TASK_NOTIFICATION = "system_task_notification" # NEW
36
38
  AGENT_IDLE = "agent_idle"
37
39
 
38
40
 
@@ -44,6 +46,7 @@ _STREAM_EVENT_TYPE_TO_PAYLOAD_CLASS: Dict[StreamEventType, Type[BaseModel]] = {
44
46
  StreamEventType.ERROR_EVENT: ErrorEventData,
45
47
  StreamEventType.TOOL_INVOCATION_APPROVAL_REQUESTED: ToolInvocationApprovalRequestedData,
46
48
  StreamEventType.TOOL_INVOCATION_AUTO_EXECUTING: ToolInvocationAutoExecutingData,
49
+ StreamEventType.SYSTEM_TASK_NOTIFICATION: SystemTaskNotificationData, # NEW
47
50
  StreamEventType.AGENT_IDLE: AgentOperationalPhaseTransitionData,
48
51
  }
49
52
 
@@ -6,6 +6,7 @@ from .base_processor import BaseSystemPromptProcessor
6
6
  from autobyteus.tools.registry import default_tool_registry, ToolDefinition
7
7
  from autobyteus.tools.usage.providers import ToolManifestProvider
8
8
  from autobyteus.prompt.prompt_template import PromptTemplate
9
+ from autobyteus.llm.providers import LLMProvider
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from autobyteus.tools.base_tool import BaseTool
@@ -18,6 +19,7 @@ class ToolManifestInjectorProcessor(BaseSystemPromptProcessor):
18
19
  Injects a tool manifest into the system prompt using Jinja2-style placeholders.
19
20
  It primarily targets the '{{tools}}' variable. It uses PromptTemplate for
20
21
  rendering and delegates manifest generation to a ToolManifestProvider.
22
+ It automatically determines whether to use XML or JSON based on the LLM provider.
21
23
  """
22
24
  # The '{{tools}}' placeholder is now handled by Jinja2 via PromptTemplate.
23
25
  DEFAULT_PREFIX_FOR_TOOLS_ONLY_PROMPT = "You have access to a set of tools. Use them by outputting the appropriate tool call format. The user can only see the output of the tool, not the call itself. The available tools are:\n\n"
@@ -42,6 +44,10 @@ class ToolManifestInjectorProcessor(BaseSystemPromptProcessor):
42
44
  if "tools" not in prompt_template.required_vars:
43
45
  return system_prompt
44
46
 
47
+ llm_provider = None
48
+ if context.llm_instance and context.llm_instance.model:
49
+ llm_provider = context.llm_instance.model.provider
50
+
45
51
  # Generate the manifest string for the 'tools' variable.
46
52
  tools_manifest: str
47
53
  if not tool_instances:
@@ -52,13 +58,10 @@ class ToolManifestInjectorProcessor(BaseSystemPromptProcessor):
52
58
  td for name in tool_instances if (td := default_tool_registry.get_tool_definition(name))
53
59
  ]
54
60
 
55
- llm_provider = context.llm_instance.model.provider if context.llm_instance and context.llm_instance.model else None
56
-
57
61
  try:
58
- # Delegate manifest generation to the provider
62
+ # Delegate manifest generation to the provider, which now handles all format logic.
59
63
  tools_manifest = self._manifest_provider.provide(
60
64
  tool_definitions=tool_definitions,
61
- use_xml=context.config.use_xml_tool_format,
62
65
  provider=llm_provider
63
66
  )
64
67
  except Exception as e:
@@ -0,0 +1 @@
1
+ # file: autobyteus/autobyteus/agent_team/__init__.py
@@ -0,0 +1,93 @@
1
+ # file: autobyteus/autobyteus/agent_team/agent_team.py
2
+ import logging
3
+ from typing import Optional
4
+
5
+ from autobyteus.agent_team.runtime.agent_team_runtime import AgentTeamRuntime
6
+ from autobyteus.agent_team.events.agent_team_events import ProcessUserMessageEvent, ToolApprovalTeamEvent
7
+ from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
8
+ from autobyteus.agent_team.phases.agent_team_operational_phase import AgentTeamOperationalPhase
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class AgentTeam:
13
+ """
14
+ User-facing facade for interacting with a managed agent team.
15
+ This class is a lightweight wrapper around an AgentTeamRuntime instance
16
+ and is typically created by an AgentTeamFactory.
17
+ """
18
+ def __init__(self, runtime: AgentTeamRuntime):
19
+ """
20
+ Initializes the AgentTeam facade.
21
+
22
+ Args:
23
+ runtime: The pre-configured and ready-to-use runtime for the agent team.
24
+ """
25
+ if not isinstance(runtime, AgentTeamRuntime):
26
+ raise TypeError(f"AgentTeam requires an AgentTeamRuntime instance, got {type(runtime).__name__}")
27
+
28
+ self._runtime = runtime
29
+ self.team_id: str = self._runtime.context.team_id
30
+ logger.info(f"AgentTeam facade created for team ID '{self.team_id}'.")
31
+
32
+ @property
33
+ def name(self) -> str:
34
+ return self._runtime.context.config.name
35
+
36
+ @property
37
+ def role(self) -> Optional[str]:
38
+ """The role of the team, for when it's used as a sub-team."""
39
+ return self._runtime.context.config.role
40
+
41
+ async def post_message(self, message: AgentInputUserMessage, target_agent_name: Optional[str] = None) -> None:
42
+ """
43
+ Submits a message to the agent team, routing it to a specific node (agent or sub-team).
44
+ If `target_agent_name` is not provided, the message is sent to the team's coordinator.
45
+ """
46
+ final_target_name = target_agent_name or self._runtime.context.config.coordinator_node.name
47
+ logger.info(f"Agent Team '{self.team_id}': post_message called. Target: '{final_target_name}'.")
48
+
49
+ if not self._runtime.is_running:
50
+ self.start()
51
+
52
+ event = ProcessUserMessageEvent(
53
+ user_message=message,
54
+ target_agent_name=final_target_name
55
+ )
56
+ await self._runtime.submit_event(event)
57
+
58
+ async def post_tool_execution_approval(
59
+ self,
60
+ agent_name: str,
61
+ tool_invocation_id: str,
62
+ is_approved: bool,
63
+ reason: Optional[str] = None
64
+ ):
65
+ """Submits a tool execution approval/denial to a specific agent in the team."""
66
+ logger.info(f"Agent Team '{self.team_id}': post_tool_execution_approval called for agent '{agent_name}'. Approved: {is_approved}.")
67
+ if not self._runtime.is_running:
68
+ logger.warning(f"Agent Team '{self.team_id}' is not running. Cannot post approval.")
69
+ return
70
+
71
+ event = ToolApprovalTeamEvent(
72
+ agent_name=agent_name,
73
+ tool_invocation_id=tool_invocation_id,
74
+ is_approved=is_approved,
75
+ reason=reason,
76
+ )
77
+ await self._runtime.submit_event(event)
78
+
79
+ def start(self) -> None:
80
+ """Starts the agent team's background worker thread."""
81
+ self._runtime.start()
82
+
83
+ async def stop(self, timeout: float = 10.0) -> None:
84
+ """Stops the agent team and all its agents."""
85
+ await self._runtime.stop(timeout)
86
+
87
+ @property
88
+ def is_running(self) -> bool:
89
+ """Checks if the agent team's worker is running."""
90
+ return self._runtime.is_running
91
+
92
+ def get_current_phase(self) -> AgentTeamOperationalPhase:
93
+ return self._runtime.context.state.current_phase