autobyteus 1.1.8__py3-none-any.whl → 1.2.0__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 (127) hide show
  1. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +6 -2
  2. autobyteus/agent/handlers/inter_agent_message_event_handler.py +17 -19
  3. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +6 -3
  4. autobyteus/agent/handlers/tool_result_event_handler.py +61 -18
  5. autobyteus/agent/handlers/user_input_message_event_handler.py +19 -10
  6. autobyteus/agent/hooks/base_phase_hook.py +17 -0
  7. autobyteus/agent/hooks/hook_registry.py +15 -27
  8. autobyteus/agent/input_processor/base_user_input_processor.py +17 -1
  9. autobyteus/agent/input_processor/processor_registry.py +15 -27
  10. autobyteus/agent/llm_response_processor/base_processor.py +17 -1
  11. autobyteus/agent/llm_response_processor/processor_registry.py +15 -24
  12. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +14 -0
  13. autobyteus/agent/message/agent_input_user_message.py +15 -2
  14. autobyteus/agent/message/send_message_to.py +1 -1
  15. autobyteus/agent/processor_option.py +17 -0
  16. autobyteus/agent/sender_type.py +1 -0
  17. autobyteus/agent/system_prompt_processor/base_processor.py +17 -1
  18. autobyteus/agent/system_prompt_processor/processor_registry.py +15 -27
  19. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +10 -0
  20. autobyteus/agent/tool_execution_result_processor/base_processor.py +17 -1
  21. autobyteus/agent/tool_execution_result_processor/processor_registry.py +15 -1
  22. autobyteus/agent/workspace/base_workspace.py +1 -1
  23. autobyteus/agent/workspace/workspace_definition.py +1 -1
  24. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +1 -1
  25. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +2 -2
  26. autobyteus/agent_team/task_notification/__init__.py +4 -0
  27. autobyteus/agent_team/task_notification/activation_policy.py +70 -0
  28. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +56 -122
  29. autobyteus/agent_team/task_notification/task_activator.py +66 -0
  30. autobyteus/cli/agent_team_tui/state.py +17 -20
  31. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +1 -1
  32. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +1 -1
  33. autobyteus/clients/__init__.py +10 -0
  34. autobyteus/clients/autobyteus_client.py +318 -0
  35. autobyteus/clients/cert_utils.py +105 -0
  36. autobyteus/clients/certificates/cert.pem +34 -0
  37. autobyteus/events/event_types.py +2 -2
  38. autobyteus/llm/api/autobyteus_llm.py +1 -1
  39. autobyteus/llm/api/gemini_llm.py +45 -54
  40. autobyteus/llm/api/qwen_llm.py +25 -0
  41. autobyteus/llm/api/zhipu_llm.py +26 -0
  42. autobyteus/llm/autobyteus_provider.py +9 -3
  43. autobyteus/llm/llm_factory.py +39 -0
  44. autobyteus/llm/ollama_provider_resolver.py +1 -0
  45. autobyteus/llm/providers.py +1 -0
  46. autobyteus/llm/token_counter/token_counter_factory.py +3 -0
  47. autobyteus/llm/token_counter/zhipu_token_counter.py +24 -0
  48. autobyteus/multimedia/audio/api/autobyteus_audio_client.py +5 -2
  49. autobyteus/multimedia/audio/api/gemini_audio_client.py +84 -153
  50. autobyteus/multimedia/audio/audio_client_factory.py +47 -22
  51. autobyteus/multimedia/audio/audio_model.py +13 -6
  52. autobyteus/multimedia/audio/autobyteus_audio_provider.py +9 -3
  53. autobyteus/multimedia/audio/base_audio_client.py +3 -1
  54. autobyteus/multimedia/image/api/autobyteus_image_client.py +13 -6
  55. autobyteus/multimedia/image/api/gemini_image_client.py +72 -130
  56. autobyteus/multimedia/image/api/openai_image_client.py +4 -2
  57. autobyteus/multimedia/image/autobyteus_image_provider.py +9 -3
  58. autobyteus/multimedia/image/base_image_client.py +6 -2
  59. autobyteus/multimedia/image/image_client_factory.py +20 -19
  60. autobyteus/multimedia/image/image_model.py +13 -6
  61. autobyteus/multimedia/providers.py +1 -0
  62. autobyteus/task_management/__init__.py +10 -10
  63. autobyteus/task_management/base_task_board.py +14 -6
  64. autobyteus/task_management/converters/__init__.py +0 -2
  65. autobyteus/task_management/converters/task_board_converter.py +7 -16
  66. autobyteus/task_management/events.py +6 -6
  67. autobyteus/task_management/in_memory_task_board.py +48 -38
  68. autobyteus/task_management/schemas/__init__.py +2 -2
  69. autobyteus/task_management/schemas/{plan_definition.py → task_definition.py} +6 -7
  70. autobyteus/task_management/schemas/task_status_report.py +1 -2
  71. autobyteus/task_management/task.py +60 -0
  72. autobyteus/task_management/tools/__init__.py +6 -2
  73. autobyteus/task_management/tools/assign_task_to.py +125 -0
  74. autobyteus/task_management/tools/get_my_tasks.py +80 -0
  75. autobyteus/task_management/tools/get_task_board_status.py +3 -3
  76. autobyteus/task_management/tools/publish_task.py +77 -0
  77. autobyteus/task_management/tools/publish_tasks.py +74 -0
  78. autobyteus/task_management/tools/update_task_status.py +5 -5
  79. autobyteus/tools/__init__.py +54 -16
  80. autobyteus/tools/base_tool.py +4 -4
  81. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +1 -1
  82. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +1 -1
  83. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +1 -1
  84. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +1 -1
  85. autobyteus/tools/browser/standalone/navigate_to.py +1 -1
  86. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +1 -1
  87. autobyteus/tools/browser/standalone/webpage_image_downloader.py +1 -1
  88. autobyteus/tools/browser/standalone/webpage_reader.py +1 -1
  89. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +1 -1
  90. autobyteus/tools/download_media_tool.py +136 -0
  91. autobyteus/tools/file/file_editor.py +200 -0
  92. autobyteus/tools/functional_tool.py +1 -1
  93. autobyteus/tools/google_search.py +1 -1
  94. autobyteus/tools/mcp/factory.py +1 -1
  95. autobyteus/tools/mcp/schema_mapper.py +1 -1
  96. autobyteus/tools/mcp/tool.py +1 -1
  97. autobyteus/tools/multimedia/__init__.py +2 -0
  98. autobyteus/tools/multimedia/audio_tools.py +10 -20
  99. autobyteus/tools/multimedia/image_tools.py +21 -22
  100. autobyteus/tools/multimedia/media_reader_tool.py +117 -0
  101. autobyteus/tools/pydantic_schema_converter.py +1 -1
  102. autobyteus/tools/registry/tool_definition.py +1 -1
  103. autobyteus/tools/timer.py +1 -1
  104. autobyteus/tools/tool_meta.py +1 -1
  105. autobyteus/tools/usage/formatters/default_json_example_formatter.py +1 -1
  106. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +1 -1
  107. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +59 -3
  108. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +1 -1
  109. autobyteus/tools/usage/formatters/google_json_example_formatter.py +1 -1
  110. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +1 -1
  111. autobyteus/tools/usage/parsers/_string_decoders.py +18 -0
  112. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +9 -1
  113. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +15 -1
  114. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +4 -1
  115. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +4 -1
  116. autobyteus/{tools → utils}/parameter_schema.py +1 -1
  117. {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/METADATA +4 -3
  118. {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/RECORD +122 -108
  119. examples/run_poem_writer.py +1 -1
  120. autobyteus/task_management/converters/task_plan_converter.py +0 -48
  121. autobyteus/task_management/task_plan.py +0 -110
  122. autobyteus/task_management/tools/publish_task_plan.py +0 -101
  123. autobyteus/tools/image_downloader.py +0 -99
  124. autobyteus/tools/pdf_downloader.py +0 -89
  125. {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/WHEEL +0 -0
  126. {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/licenses/LICENSE +0 -0
  127. {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/top_level.txt +0 -0
@@ -5,23 +5,23 @@ Defines the Pydantic models for events emitted by a TaskBoard.
5
5
  from typing import List, Optional
6
6
  from pydantic import BaseModel
7
7
 
8
- from autobyteus.task_management.task_plan import TaskPlan
8
+ from autobyteus.task_management.task import Task
9
9
  from autobyteus.task_management.base_task_board import TaskStatus
10
10
  from .deliverable import FileDeliverable
11
11
 
12
12
  class BaseTaskBoardEvent(BaseModel):
13
13
  """Base class for all task board events."""
14
14
  team_id: str
15
- plan_id: Optional[str]
16
15
 
17
- class TaskPlanPublishedEvent(BaseTaskBoardEvent):
18
- """Payload for when a new task plan is published to the board."""
19
- plan: TaskPlan
16
+ class TasksAddedEvent(BaseTaskBoardEvent):
17
+ """
18
+ Payload for when one or more tasks are added to the board.
19
+ """
20
+ tasks: List[Task]
20
21
 
21
22
  class TaskStatusUpdatedEvent(BaseTaskBoardEvent):
22
23
  """Payload for when a task's status is updated."""
23
24
  task_id: str
24
25
  new_status: TaskStatus
25
26
  agent_name: str
26
- # This field is added to ensure listeners get the full state, including deliverables.
27
27
  deliverables: Optional[List[FileDeliverable]] = None
@@ -8,9 +8,9 @@ from typing import Optional, List, Dict, Any
8
8
  from enum import Enum
9
9
 
10
10
  from autobyteus.events.event_types import EventType
11
- from .task_plan import TaskPlan, Task
11
+ from .task import Task
12
12
  from .base_task_board import BaseTaskBoard, TaskStatus
13
- from .events import TaskPlanPublishedEvent, TaskStatusUpdatedEvent
13
+ from .events import TasksAddedEvent, TaskStatusUpdatedEvent
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
@@ -23,37 +23,63 @@ class InMemoryTaskBoard(BaseTaskBoard):
23
23
  """
24
24
  Initializes the InMemoryTaskBoard.
25
25
  """
26
- # BaseTaskBoard now handles EventEmitter initialization
27
26
  super().__init__(team_id=team_id)
28
- self.current_plan: Optional[TaskPlan] = None
29
27
  self.task_statuses: Dict[str, TaskStatus] = {}
30
28
  self._task_map: Dict[str, Task] = {}
31
29
  logger.info(f"InMemoryTaskBoard initialized for team '{self.team_id}'.")
32
30
 
33
- def load_task_plan(self, plan: TaskPlan) -> bool:
31
+ def add_tasks(self, tasks: List[Task]) -> bool:
34
32
  """
35
- Loads a new plan onto the board, resetting its state and emitting an event.
33
+ Adds new tasks to the board. This is an additive-only operation.
36
34
  """
37
- if not isinstance(plan, TaskPlan):
38
- logger.error(f"Team '{self.team_id}': Failed to load task plan. Provided object is not a TaskPlan.")
39
- return False
35
+ for task in tasks:
36
+ self.tasks.append(task)
37
+ self.task_statuses[task.task_id] = TaskStatus.NOT_STARTED
38
+ self._task_map[task.task_id] = task
40
39
 
41
- self.current_plan = plan
42
- self.task_statuses = {task.task_id: TaskStatus.NOT_STARTED for task in plan.tasks}
43
- self._task_map = {task.task_id: task for task in plan.tasks}
44
-
45
- logger.info(f"Team '{self.team_id}': New TaskPlan '{plan.plan_id}' loaded. Emitting event.")
40
+ self._hydrate_all_dependencies()
41
+ logger.info(f"Team '{self.team_id}': Added {len(tasks)} new task(s) to the board. Emitting TasksAddedEvent.")
46
42
 
47
- # Emit event
48
- event_payload = TaskPlanPublishedEvent(
43
+ event_payload = TasksAddedEvent(
49
44
  team_id=self.team_id,
50
- plan_id=plan.plan_id,
51
- plan=plan
45
+ tasks=tasks,
52
46
  )
53
- self.emit(EventType.TASK_BOARD_PLAN_PUBLISHED, payload=event_payload)
54
-
47
+ self.emit(EventType.TASK_BOARD_TASKS_ADDED, payload=event_payload)
55
48
  return True
56
49
 
50
+ def add_task(self, task: Task) -> bool:
51
+ """
52
+ Adds a single new task to the board by wrapping it in a list and calling add_tasks.
53
+ """
54
+ return self.add_tasks([task])
55
+
56
+ def _hydrate_all_dependencies(self):
57
+ """
58
+ Re-calculates all dependencies to ensure they are all valid task_ids.
59
+ This robustly handles dependencies that are already IDs and those that are names.
60
+ """
61
+ name_to_id_map = {task.task_name: task.task_id for task in self.tasks}
62
+ all_task_ids = set(self._task_map.keys())
63
+
64
+ for task in self.tasks:
65
+ if not task.dependencies:
66
+ continue
67
+
68
+ resolved_deps = []
69
+ for dep in task.dependencies:
70
+ # Case 1: The dependency is already a valid task_id on the board.
71
+ if dep in all_task_ids:
72
+ resolved_deps.append(dep)
73
+ # Case 2: The dependency is a task_name that can be resolved.
74
+ elif dep in name_to_id_map:
75
+ resolved_deps.append(name_to_id_map[dep])
76
+ # Case 3: The dependency is invalid.
77
+ else:
78
+ logger.warning(f"Team '{self.team_id}': Dependency '{dep}' for task '{task.task_name}' could not be resolved to a known task ID or name.")
79
+
80
+ task.dependencies = resolved_deps
81
+
82
+
57
83
  def update_task_status(self, task_id: str, status: TaskStatus, agent_name: str) -> bool:
58
84
  """
59
85
  Updates the status of a specific task and emits an event.
@@ -67,40 +93,27 @@ class InMemoryTaskBoard(BaseTaskBoard):
67
93
  log_msg = f"Team '{self.team_id}': Status of task '{task_id}' updated from '{old_status.value if isinstance(old_status, Enum) else old_status}' to '{status.value}' by agent '{agent_name}'."
68
94
  logger.info(log_msg)
69
95
 
70
- # Find the task to get its deliverables for the event payload
71
96
  task = self._task_map.get(task_id)
72
97
  task_deliverables = task.file_deliverables if task else None
73
98
 
74
- # Emit event
75
99
  event_payload = TaskStatusUpdatedEvent(
76
100
  team_id=self.team_id,
77
- plan_id=self.current_plan.plan_id if self.current_plan else None,
78
101
  task_id=task_id,
79
102
  new_status=status,
80
103
  agent_name=agent_name,
81
104
  deliverables=task_deliverables
82
105
  )
83
106
  self.emit(EventType.TASK_BOARD_STATUS_UPDATED, payload=event_payload)
84
-
85
107
  return True
86
108
 
87
109
  def get_status_overview(self) -> Dict[str, Any]:
88
110
  """
89
111
  Returns a serializable dictionary of the board's current state.
112
+ The overall_goal is now fetched from the context via the converter.
90
113
  """
91
- if not self.current_plan:
92
- return {
93
- "plan_id": None,
94
- "overall_goal": None,
95
- "task_statuses": {},
96
- "tasks": []
97
- }
98
-
99
114
  return {
100
- "plan_id": self.current_plan.plan_id,
101
- "overall_goal": self.current_plan.overall_goal,
102
115
  "task_statuses": {task_id: status.value for task_id, status in self.task_statuses.items()},
103
- "tasks": [task.model_dump() for task in self.current_plan.tasks]
116
+ "tasks": [task.model_dump() for task in self.tasks]
104
117
  }
105
118
 
106
119
  def get_next_runnable_tasks(self) -> List[Task]:
@@ -108,9 +121,6 @@ class InMemoryTaskBoard(BaseTaskBoard):
108
121
  Calculates which tasks can be executed now based on dependencies and statuses.
109
122
  """
110
123
  runnable_tasks: List[Task] = []
111
- if not self.current_plan:
112
- return runnable_tasks
113
-
114
124
  for task_id, status in self.task_statuses.items():
115
125
  if status == TaskStatus.NOT_STARTED:
116
126
  task = self._task_map.get(task_id)
@@ -2,12 +2,12 @@
2
2
  """
3
3
  Exposes the public schema models for the task management module.
4
4
  """
5
- from .plan_definition import TaskPlanDefinitionSchema, TaskDefinitionSchema
5
+ from .task_definition import TasksDefinitionSchema, TaskDefinitionSchema
6
6
  from .task_status_report import TaskStatusReportSchema, TaskStatusReportItemSchema
7
7
  from .deliverable_schema import FileDeliverableSchema
8
8
 
9
9
  __all__ = [
10
- "TaskPlanDefinitionSchema",
10
+ "TasksDefinitionSchema",
11
11
  "TaskDefinitionSchema",
12
12
  "TaskStatusReportSchema",
13
13
  "TaskStatusReportItemSchema",
@@ -1,5 +1,5 @@
1
1
  """
2
- Defines the Pydantic models for task plan "definitions".
2
+ Defines the Pydantic models for task "definitions".
3
3
 
4
4
  These models represent the exact structure that an LLM is expected to generate.
5
5
  They serve as a blueprint or definition for a plan, which the system then uses
@@ -13,20 +13,19 @@ class TaskDefinitionSchema(BaseModel):
13
13
  """A Pydantic model representing a single task as defined by an LLM."""
14
14
  task_name: str = Field(..., description="A short, unique, descriptive name for this task within the plan (e.g., 'setup_project', 'implement_scraper'). Used for defining dependencies.")
15
15
  assignee_name: str = Field(..., description="The name of the agent or sub-team assigned to this task.")
16
- description: str = Field(..., description="A detailed description of the task.")
16
+ description: str = Field(..., description="A clear, detailed, and unambiguous description of what this task entails. Provide all necessary context for the assignee to complete the work. For example, if the task involves a file, specify its full, absolute path. If it requires creating a file, specify where it should be saved. Mention any specific requirements or expected outputs.")
17
17
  dependencies: List[str] = Field(
18
18
  default_factory=list,
19
19
  description="A list of 'task_name' values for tasks that must be completed first."
20
20
  )
21
21
 
22
- class TaskPlanDefinitionSchema(BaseModel):
23
- """A Pydantic model representing a full task plan as generated by an LLM."""
24
- overall_goal: str = Field(..., description="The high-level objective of the entire plan.")
25
- tasks: List[TaskDefinitionSchema] = Field(..., description="The list of tasks that make up this plan.")
22
+ class TasksDefinitionSchema(BaseModel):
23
+ """A Pydantic model representing a list of tasks as generated by an LLM."""
24
+ tasks: List[TaskDefinitionSchema] = Field(..., description="The list of tasks to be published.")
26
25
 
27
26
  @field_validator('tasks')
28
27
  def task_names_must_be_unique(cls, tasks: List[TaskDefinitionSchema]) -> List[TaskDefinitionSchema]:
29
- """Ensures that the LLM-provided task_names are unique within the plan."""
28
+ """Ensures that the LLM-provided task_names are unique within this list."""
30
29
  seen_names = set()
31
30
  for task in tasks:
32
31
  if task.task_name in seen_names:
@@ -16,12 +16,11 @@ class TaskStatusReportItemSchema(BaseModel):
16
16
  """Represents the status of a single task in an LLM-friendly format."""
17
17
  task_name: str = Field(..., description="The unique, descriptive name for this task.")
18
18
  assignee_name: str = Field(..., description="The name of the agent or sub-team assigned to this task.")
19
- description: str = Field(..., description="A detailed description of the task.")
19
+ description: str = Field(..., description="A clear, detailed, and unambiguous description of what this task entails. Provide all necessary context for the assignee to complete the work. For example, if the task involves a file, specify its full, absolute path. If it requires creating a file, specify where it should be saved. Mention any specific requirements or expected outputs.")
20
20
  dependencies: List[str] = Field(..., description="A list of 'task_name' values for tasks that must be completed first.")
21
21
  status: TaskStatus = Field(..., description="The current status of this task.")
22
22
  file_deliverables: List[FileDeliverable] = Field(default_factory=list, description="A list of files submitted as deliverables for this task.")
23
23
 
24
24
  class TaskStatusReportSchema(BaseModel):
25
25
  """Represents a full task board status report in an LLM-friendly format."""
26
- overall_goal: str = Field(..., description="The high-level objective of the entire plan.")
27
26
  tasks: List[TaskStatusReportItemSchema] = Field(..., description="The list of tasks and their current statuses.")
@@ -0,0 +1,60 @@
1
+ # file: autobyteus/autobyteus/task_management/task.py
2
+ """
3
+ Defines the data structures for a task.
4
+ """
5
+ import logging
6
+ import uuid
7
+ from typing import List, Any
8
+ from pydantic import BaseModel, Field, model_validator
9
+
10
+ # To avoid circular import, we use a string forward reference.
11
+ from typing import TYPE_CHECKING
12
+ if TYPE_CHECKING:
13
+ from autobyteus.task_management.deliverable import FileDeliverable
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ def generate_task_id():
18
+ """Generates a unique task identifier."""
19
+ return f"task_{uuid.uuid4().hex}"
20
+
21
+ class Task(BaseModel):
22
+ """
23
+ Represents a single, discrete unit of work.
24
+ """
25
+ task_name: str = Field(..., description="A short, unique, descriptive name for this task within the plan (e.g., 'setup_project', 'implement_scraper'). Used for defining dependencies.")
26
+
27
+ task_id: str = Field(default_factory=generate_task_id, description="A unique system-generated identifier for this task within the plan.")
28
+
29
+ assignee_name: str = Field(..., description="The unique name of the agent or sub-team responsible for executing this task (e.g., 'SoftwareEngineer', 'ResearchTeam').")
30
+ description: str = Field(..., description="A clear and concise description of what this task entails.")
31
+
32
+ dependencies: List[str] = Field(
33
+ default_factory=list,
34
+ description="A list of 'task_name' values for tasks that must be completed before this one can be started."
35
+ )
36
+
37
+ # This is the updated field as per user request.
38
+ file_deliverables: List["FileDeliverable"] = Field(
39
+ default_factory=list,
40
+ description="A list of file deliverables that were produced as a result of completing this task."
41
+ )
42
+
43
+ @model_validator(mode='before')
44
+ @classmethod
45
+ def handle_local_id_compatibility(cls, data: Any) -> Any:
46
+ """Handles backward compatibility for the 'local_id' field."""
47
+ if isinstance(data, dict) and 'local_id' in data:
48
+ data['task_name'] = data.pop('local_id')
49
+ # Compatibility for old artifact field
50
+ if isinstance(data, dict) and 'produced_artifact_ids' in data:
51
+ del data['produced_artifact_ids']
52
+ return data
53
+
54
+ def model_post_init(self, __context: Any) -> None:
55
+ """Called after the model is initialized and validated."""
56
+ logger.debug(f"Task created: Name='{self.task_name}', SystemID='{self.task_id}', Assignee='{self.assignee_name}'")
57
+
58
+ # This is necessary for Pydantic v2 to correctly handle the recursive model
59
+ from autobyteus.task_management.deliverable import FileDeliverable
60
+ Task.model_rebuild()
@@ -4,11 +4,15 @@ This package contains the class-based tools related to task and project
4
4
  management within an agent team.
5
5
  """
6
6
  from .get_task_board_status import GetTaskBoardStatus
7
- from .publish_task_plan import PublishTaskPlan
7
+ from .publish_tasks import PublishTasks
8
+ from .publish_task import PublishTask
8
9
  from .update_task_status import UpdateTaskStatus
10
+ from .assign_task_to import AssignTaskTo
9
11
 
10
12
  __all__ = [
11
13
  "GetTaskBoardStatus",
12
- "PublishTaskPlan",
14
+ "PublishTasks",
15
+ "PublishTask",
13
16
  "UpdateTaskStatus",
17
+ "AssignTaskTo",
14
18
  ]
@@ -0,0 +1,125 @@
1
+ # file: autobyteus/autobyteus/task_management/tools/assign_task_to.py
2
+ import logging
3
+ from typing import TYPE_CHECKING, Optional, Any
4
+
5
+ from pydantic import ValidationError
6
+
7
+ from autobyteus.tools.base_tool import BaseTool
8
+ from autobyteus.tools.tool_category import ToolCategory
9
+ from autobyteus.utils.parameter_schema import ParameterSchema
10
+ from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
11
+ from autobyteus.task_management.schemas import TaskDefinitionSchema
12
+ from autobyteus.task_management.task import Task
13
+
14
+ if TYPE_CHECKING:
15
+ from autobyteus.agent.context import AgentContext
16
+ from autobyteus.agent_team.context import AgentTeamContext
17
+ from autobyteus.agent_team.context.team_manager import TeamManager
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ class AssignTaskTo(BaseTool):
22
+ """A tool for one agent to directly create and assign a single task to another agent."""
23
+
24
+ CATEGORY = ToolCategory.TASK_MANAGEMENT
25
+
26
+ @classmethod
27
+ def get_name(cls) -> str:
28
+ return "AssignTaskTo"
29
+
30
+ @classmethod
31
+ def get_description(cls) -> str:
32
+ return (
33
+ "Creates and assigns a single new task to a specific team member, and sends them a direct notification "
34
+ "with the task details. Use this to delegate a well-defined piece of work you have identified."
35
+ )
36
+
37
+ @classmethod
38
+ def get_argument_schema(cls) -> Optional[ParameterSchema]:
39
+ # The schema is the same as for defining a single task.
40
+ return pydantic_to_parameter_schema(TaskDefinitionSchema)
41
+
42
+ async def _execute(self, context: 'AgentContext', **kwargs: Any) -> str:
43
+ """
44
+ Executes the tool by adding the task to the central TaskBoard and then
45
+ sending a direct message to the assignee with the task's details.
46
+ """
47
+ agent_name = context.config.name
48
+ task_name = kwargs.get("task_name", "unnamed task")
49
+ assignee_name = kwargs.get("assignee_name")
50
+ logger.info(f"Agent '{agent_name}' is executing AssignTaskTo for task '{task_name}' assigned to '{assignee_name}'.")
51
+
52
+ # --- Get Team Context and Task Board ---
53
+ team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
54
+ if not team_context:
55
+ error_msg = "Error: Team context is not available. Cannot access the task board or send messages."
56
+ logger.error(f"Agent '{agent_name}': {error_msg}")
57
+ return error_msg
58
+
59
+ task_board = getattr(team_context.state, 'task_board', None)
60
+ if not task_board:
61
+ error_msg = "Error: Task board has not been initialized for this team."
62
+ logger.error(f"Agent '{agent_name}': {error_msg}")
63
+ return error_msg
64
+
65
+ # --- Action 1: Add the task to the Task Board ---
66
+ try:
67
+ task_def_schema = TaskDefinitionSchema(**kwargs)
68
+ new_task = Task(**task_def_schema.model_dump())
69
+ except (ValidationError, ValueError) as e:
70
+ error_msg = f"Invalid task definition provided: {e}"
71
+ logger.warning(f"Agent '{agent_name}' provided an invalid definition for AssignTaskTo: {error_msg}")
72
+ return f"Error: {error_msg}"
73
+
74
+ if not task_board.add_task(new_task):
75
+ error_msg = f"Failed to publish task '{new_task.task_name}' to the board for an unknown reason."
76
+ logger.error(f"Agent '{agent_name}': {error_msg}")
77
+ return f"Error: {error_msg}"
78
+
79
+ logger.info(f"Agent '{agent_name}' successfully published task '{new_task.task_name}' to the task board.")
80
+
81
+ # --- Action 2: Send a direct notification message to the assignee ---
82
+ team_manager: Optional['TeamManager'] = team_context.team_manager
83
+ if not team_manager:
84
+ # This is a degraded state, but the primary action (publishing) succeeded.
85
+ warning_msg = (f"Successfully published task '{new_task.task_name}', but could not send a direct notification "
86
+ "because the TeamManager is not available.")
87
+ logger.warning(f"Agent '{agent_name}': {warning_msg}")
88
+ return warning_msg
89
+
90
+ try:
91
+ # Local import to break potential circular dependency at module load time.
92
+ from autobyteus.agent_team.events.agent_team_events import InterAgentMessageRequestEvent
93
+
94
+ notification_content = (
95
+ f"You have been assigned a new task directly from agent '{agent_name}'.\n\n"
96
+ f"**Task Name**: '{new_task.task_name}'\n"
97
+ f"**Description**: {new_task.description}\n"
98
+ )
99
+ if new_task.dependencies:
100
+ # Resolve dependency names for the message
101
+ id_to_name_map = {task.task_id: task.task_name for task in task_board.tasks}
102
+ dep_names = [id_to_name_map.get(dep_id, str(dep_id)) for dep_id in new_task.dependencies]
103
+ notification_content += f"**Dependencies**: {', '.join(dep_names)}\n"
104
+
105
+ notification_content += "\nThis task has been logged on the team's task board. You can begin work when its dependencies are met."
106
+
107
+ event = InterAgentMessageRequestEvent(
108
+ sender_agent_id=context.agent_id,
109
+ recipient_name=new_task.assignee_name,
110
+ content=notification_content,
111
+ message_type="task_assignment"
112
+ )
113
+
114
+ await team_manager.dispatch_inter_agent_message_request(event)
115
+ logger.info(f"Agent '{agent_name}' successfully dispatched a notification message for task '{new_task.task_name}' to '{new_task.assignee_name}'.")
116
+
117
+ except Exception as e:
118
+ # Again, this is a degraded state. The main goal was achieved.
119
+ warning_msg = (f"Successfully published task '{new_task.task_name}', but failed to send the direct notification message. "
120
+ f"Error: {e}")
121
+ logger.error(f"Agent '{agent_name}': {warning_msg}", exc_info=True)
122
+ return warning_msg
123
+
124
+ success_msg = f"Successfully assigned task '{new_task.task_name}' to agent '{new_task.assignee_name}' and sent a notification."
125
+ return success_msg
@@ -0,0 +1,80 @@
1
+ # file: autobyteus/autobyteus/task_management/tools/get_my_tasks.py
2
+ import json
3
+ import logging
4
+ from typing import TYPE_CHECKING, Optional, List
5
+
6
+ from autobyteus.tools.base_tool import BaseTool
7
+ from autobyteus.tools.tool_category import ToolCategory
8
+ from autobyteus.task_management.schemas import TaskDefinitionSchema
9
+ from autobyteus.task_management.base_task_board import TaskStatus
10
+
11
+ if TYPE_CHECKING:
12
+ from autobyteus.agent.context import AgentContext
13
+ from autobyteus.agent_team.context import AgentTeamContext
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class GetMyTasks(BaseTool):
18
+ """A tool for an agent to inspect its own assigned tasks from the central TaskBoard."""
19
+
20
+ CATEGORY = ToolCategory.TASK_MANAGEMENT
21
+
22
+ @classmethod
23
+ def get_name(cls) -> str:
24
+ return "GetMyTasks"
25
+
26
+ @classmethod
27
+ def get_description(cls) -> str:
28
+ return (
29
+ "Retrieves the list of tasks currently assigned to you from the team's shared task board. "
30
+ "This is your personal to-do list. Use this to understand your current workload and decide what to do next."
31
+ )
32
+
33
+ @classmethod
34
+ def get_argument_schema(cls) -> Optional[None]:
35
+ # This tool takes no arguments.
36
+ return None
37
+
38
+ async def _execute(self, context: 'AgentContext') -> str:
39
+ """
40
+ Executes the tool by fetching tasks from the team's TaskBoard and
41
+ filtering them for the current agent.
42
+ """
43
+ agent_name = context.config.name
44
+ logger.info(f"Agent '{agent_name}' is executing GetMyTasks.")
45
+
46
+ team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
47
+ if not team_context:
48
+ error_msg = "Error: Team context is not available. Cannot access the task board."
49
+ logger.error(f"Agent '{agent_name}': {error_msg}")
50
+ return error_msg
51
+
52
+ task_board = getattr(team_context.state, 'task_board', None)
53
+ if not task_board:
54
+ error_msg = "Error: Task board has not been initialized for this team."
55
+ logger.error(f"Agent '{agent_name}': {error_msg}")
56
+ return error_msg
57
+
58
+ # Filter the tasks from the central board for this agent.
59
+ # An agent should only see tasks that are specifically for them and are ready to be worked on.
60
+ my_tasks = [
61
+ task for task in task_board.tasks
62
+ if task.assignee_name == agent_name and task_board.task_statuses.get(task.task_id) == TaskStatus.QUEUED
63
+ ]
64
+
65
+ if not my_tasks:
66
+ return "Your personal task queue is empty. You have no new tasks assigned and ready to be started."
67
+
68
+ try:
69
+ # Convert the internal Task objects back to the LLM-friendly schema.
70
+ tasks_for_llm = [
71
+ TaskDefinitionSchema.model_validate(task).model_dump() for task in my_tasks
72
+ ]
73
+
74
+ logger.info(f"Agent '{agent_name}' retrieved {len(tasks_for_llm)} tasks from the central task board.")
75
+ return json.dumps(tasks_for_llm, indent=2)
76
+
77
+ except Exception as e:
78
+ error_msg = f"An unexpected error occurred while formatting your tasks: {e}"
79
+ logger.error(f"Agent '{agent_name}': {error_msg}", exc_info=True)
80
+ return f"Error: {error_msg}"
@@ -25,8 +25,8 @@ class GetTaskBoardStatus(BaseTool):
25
25
  @classmethod
26
26
  def get_description(cls) -> str:
27
27
  return (
28
- "Retrieves the current status of the team's task board, including the overall goal "
29
- "and the status of all individual tasks. Returns the status as a structured, LLM-friendly JSON string."
28
+ "Retrieves the current status of the team's task board, including the status of all individual tasks. "
29
+ "Returns the status as a structured, LLM-friendly JSON string."
30
30
  )
31
31
 
32
32
  @classmethod
@@ -57,7 +57,7 @@ class GetTaskBoardStatus(BaseTool):
57
57
  status_report_schema = TaskBoardConverter.to_schema(task_board)
58
58
 
59
59
  if not status_report_schema:
60
- return "The task board is currently empty. No plan has been published."
60
+ return "The task board is currently empty. No tasks have been published."
61
61
 
62
62
  logger.info(f"Agent '{context.agent_id}' successfully retrieved and formatted task board status.")
63
63
  return status_report_schema.model_dump_json(indent=2)
@@ -0,0 +1,77 @@
1
+ # file: autobyteus/autobyteus/task_management/tools/publish_task.py
2
+ import logging
3
+ from typing import TYPE_CHECKING, Optional, Dict, Any
4
+
5
+ from pydantic import ValidationError
6
+
7
+ from autobyteus.tools.base_tool import BaseTool
8
+ from autobyteus.tools.tool_category import ToolCategory
9
+ from autobyteus.utils.parameter_schema import ParameterSchema
10
+ from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
11
+ from autobyteus.task_management.schemas import TaskDefinitionSchema
12
+ from autobyteus.task_management.task import Task
13
+
14
+ if TYPE_CHECKING:
15
+ from autobyteus.agent.context import AgentContext
16
+ from autobyteus.agent_team.context import AgentTeamContext
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ class PublishTask(BaseTool):
21
+ """A tool for any agent to add a single new task to the team's task board."""
22
+
23
+ CATEGORY = ToolCategory.TASK_MANAGEMENT
24
+
25
+ @classmethod
26
+ def get_name(cls) -> str:
27
+ return "PublishTask"
28
+
29
+ @classmethod
30
+ def get_description(cls) -> str:
31
+ return (
32
+ "Adds a single new task to the team's shared task board. This is an additive action "
33
+ "and does not affect existing tasks. Use this to create follow-up tasks or delegate new work."
34
+ )
35
+
36
+ @classmethod
37
+ def get_argument_schema(cls) -> Optional[ParameterSchema]:
38
+ # The schema for this tool is effectively the schema of a single task definition.
39
+ return pydantic_to_parameter_schema(TaskDefinitionSchema)
40
+
41
+ async def _execute(self, context: 'AgentContext', **kwargs: Any) -> str:
42
+ """
43
+ Executes the tool by validating the task object and adding it to the board.
44
+ """
45
+ agent_name = context.config.name
46
+ task_name = kwargs.get("task_name", "unnamed task")
47
+ logger.info(f"Agent '{agent_name}' is executing PublishTask for task '{task_name}'.")
48
+
49
+ team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
50
+ if not team_context:
51
+ error_msg = "Error: Team context is not available. Cannot access the task board."
52
+ logger.error(f"Agent '{agent_name}': {error_msg}")
53
+ return error_msg
54
+
55
+ task_board = getattr(team_context.state, 'task_board', None)
56
+ if not task_board:
57
+ error_msg = "Error: Task board has not been initialized for this team."
58
+ logger.error(f"Agent '{agent_name}': {error_msg}")
59
+ return error_msg
60
+
61
+ try:
62
+ task_def_schema = TaskDefinitionSchema(**kwargs)
63
+ new_task = Task(**task_def_schema.model_dump())
64
+ except (ValidationError, ValueError) as e:
65
+ error_msg = f"Invalid task definition provided: {e}"
66
+ logger.warning(f"Agent '{agent_name}' provided an invalid definition for PublishTask: {error_msg}")
67
+ return f"Error: {error_msg}"
68
+
69
+ if task_board.add_task(new_task):
70
+ success_msg = f"Successfully published new task '{new_task.task_name}' to the task board."
71
+ logger.info(f"Agent '{agent_name}': {success_msg}")
72
+ return success_msg
73
+ else:
74
+ # This path is less likely now but kept for robustness.
75
+ error_msg = "Failed to publish task to the board for an unknown reason."
76
+ logger.error(f"Agent '{agent_name}': {error_msg}")
77
+ return f"Error: {error_msg}"