autobyteus 1.1.9__py3-none-any.whl → 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/context/agent_runtime_state.py +4 -0
- autobyteus/agent/events/notifiers.py +5 -1
- autobyteus/agent/message/send_message_to.py +5 -4
- autobyteus/agent/streaming/agent_event_stream.py +5 -0
- autobyteus/agent/streaming/stream_event_payloads.py +25 -0
- autobyteus/agent/streaming/stream_events.py +13 -1
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +4 -4
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +12 -12
- autobyteus/agent_team/context/agent_team_runtime_state.py +2 -2
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +4 -4
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +3 -3
- autobyteus/agent_team/streaming/agent_team_stream_events.py +8 -8
- autobyteus/agent_team/task_notification/activation_policy.py +1 -1
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +22 -22
- autobyteus/agent_team/task_notification/task_notification_mode.py +1 -1
- autobyteus/cli/agent_team_tui/app.py +4 -4
- autobyteus/cli/agent_team_tui/state.py +8 -8
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +3 -3
- autobyteus/cli/agent_team_tui/widgets/shared.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/{task_board_panel.py → task_plan_panel.py} +5 -5
- autobyteus/clients/__init__.py +10 -0
- autobyteus/clients/autobyteus_client.py +318 -0
- autobyteus/clients/cert_utils.py +105 -0
- autobyteus/clients/certificates/cert.pem +34 -0
- autobyteus/events/event_types.py +4 -3
- autobyteus/llm/api/autobyteus_llm.py +1 -1
- autobyteus/llm/api/zhipu_llm.py +26 -0
- autobyteus/llm/autobyteus_provider.py +1 -1
- autobyteus/llm/llm_factory.py +23 -0
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +3 -0
- autobyteus/llm/token_counter/zhipu_token_counter.py +24 -0
- autobyteus/multimedia/audio/api/__init__.py +3 -2
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +1 -1
- autobyteus/multimedia/audio/api/openai_audio_client.py +112 -0
- autobyteus/multimedia/audio/audio_client_factory.py +37 -0
- autobyteus/multimedia/audio/autobyteus_audio_provider.py +1 -1
- autobyteus/multimedia/image/api/autobyteus_image_client.py +1 -1
- autobyteus/multimedia/image/autobyteus_image_provider.py +1 -1
- autobyteus/multimedia/image/image_client_factory.py +1 -1
- autobyteus/task_management/__init__.py +44 -20
- autobyteus/task_management/{base_task_board.py → base_task_plan.py} +16 -13
- autobyteus/task_management/converters/__init__.py +2 -2
- autobyteus/task_management/converters/{task_board_converter.py → task_plan_converter.py} +13 -13
- autobyteus/task_management/events.py +7 -7
- autobyteus/task_management/{in_memory_task_board.py → in_memory_task_plan.py} +34 -22
- autobyteus/task_management/schemas/__init__.py +3 -0
- autobyteus/task_management/schemas/task_definition.py +1 -1
- autobyteus/task_management/schemas/task_status_report.py +3 -3
- autobyteus/task_management/schemas/todo_definition.py +15 -0
- autobyteus/task_management/todo.py +29 -0
- autobyteus/task_management/todo_list.py +75 -0
- autobyteus/task_management/tools/__init__.py +25 -7
- autobyteus/task_management/tools/task_tools/__init__.py +19 -0
- autobyteus/task_management/tools/task_tools/assign_task_to.py +125 -0
- autobyteus/task_management/tools/{publish_task.py → task_tools/create_task.py} +16 -18
- autobyteus/task_management/tools/{publish_tasks.py → task_tools/create_tasks.py} +19 -19
- autobyteus/task_management/tools/{get_my_tasks.py → task_tools/get_my_tasks.py} +15 -15
- autobyteus/task_management/tools/{get_task_board_status.py → task_tools/get_task_plan_status.py} +16 -16
- autobyteus/task_management/tools/{update_task_status.py → task_tools/update_task_status.py} +16 -16
- autobyteus/task_management/tools/todo_tools/__init__.py +18 -0
- autobyteus/task_management/tools/todo_tools/add_todo.py +78 -0
- autobyteus/task_management/tools/todo_tools/create_todo_list.py +79 -0
- autobyteus/task_management/tools/todo_tools/get_todo_list.py +55 -0
- autobyteus/task_management/tools/todo_tools/update_todo_status.py +85 -0
- autobyteus/tools/__init__.py +61 -21
- autobyteus/tools/bash/bash_executor.py +3 -3
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +5 -5
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +4 -4
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -3
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -3
- autobyteus/tools/browser/standalone/navigate_to.py +13 -9
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +9 -5
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +10 -6
- autobyteus/tools/browser/standalone/webpage_reader.py +13 -9
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +9 -5
- autobyteus/tools/file/__init__.py +13 -0
- autobyteus/tools/file/edit_file.py +200 -0
- autobyteus/tools/file/list_directory.py +168 -0
- autobyteus/tools/file/{file_reader.py → read_file.py} +3 -3
- autobyteus/tools/file/search_files.py +188 -0
- autobyteus/tools/file/{file_writer.py → write_file.py} +3 -3
- autobyteus/tools/functional_tool.py +10 -8
- autobyteus/tools/mcp/tool.py +3 -3
- autobyteus/tools/mcp/tool_registrar.py +5 -2
- autobyteus/tools/multimedia/__init__.py +2 -1
- autobyteus/tools/multimedia/audio_tools.py +2 -2
- autobyteus/tools/multimedia/download_media_tool.py +136 -0
- autobyteus/tools/multimedia/image_tools.py +4 -4
- autobyteus/tools/multimedia/media_reader_tool.py +1 -1
- autobyteus/tools/registry/tool_definition.py +66 -13
- autobyteus/tools/registry/tool_registry.py +29 -0
- autobyteus/tools/search/__init__.py +17 -0
- autobyteus/tools/search/base_strategy.py +35 -0
- autobyteus/tools/search/client.py +24 -0
- autobyteus/tools/search/factory.py +81 -0
- autobyteus/tools/search/google_cse_strategy.py +68 -0
- autobyteus/tools/search/providers.py +10 -0
- autobyteus/tools/search/serpapi_strategy.py +65 -0
- autobyteus/tools/search/serper_strategy.py +87 -0
- autobyteus/tools/search_tool.py +83 -0
- autobyteus/tools/timer.py +4 -0
- autobyteus/tools/tool_meta.py +4 -24
- autobyteus/tools/usage/parsers/_string_decoders.py +18 -0
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +9 -1
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +15 -1
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +4 -1
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +4 -1
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +1 -2
- {autobyteus-1.1.9.dist-info → autobyteus-1.2.1.dist-info}/METADATA +7 -6
- {autobyteus-1.1.9.dist-info → autobyteus-1.2.1.dist-info}/RECORD +117 -94
- examples/run_agentic_software_engineer.py +239 -0
- examples/run_poem_writer.py +3 -3
- autobyteus/person/__init__.py +0 -0
- autobyteus/person/examples/__init__.py +0 -0
- autobyteus/person/examples/sample_persons.py +0 -14
- autobyteus/person/examples/sample_roles.py +0 -14
- autobyteus/person/person.py +0 -29
- autobyteus/person/role.py +0 -14
- autobyteus/tools/google_search.py +0 -149
- autobyteus/tools/image_downloader.py +0 -99
- autobyteus/tools/pdf_downloader.py +0 -89
- {autobyteus-1.1.9.dist-info → autobyteus-1.2.1.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.9.dist-info → autobyteus-1.2.1.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.1.9.dist-info → autobyteus-1.2.1.dist-info}/top_level.txt +0 -0
autobyteus/tools/tool_meta.py
CHANGED
|
@@ -35,26 +35,8 @@ class ToolMeta(ABCMeta):
|
|
|
35
35
|
logger.error(f"Tool class {name} ({tool_name}) must return a valid string from get_description(). Skipping registration.")
|
|
36
36
|
return
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
argument_schema = cls.get_argument_schema()
|
|
41
|
-
if argument_schema is not None and not isinstance(argument_schema, ParameterSchema):
|
|
42
|
-
logger.error(f"Tool class {name} ({tool_name}) get_argument_schema() must return a ParameterSchema or None. Got {type(argument_schema)}. Skipping registration.")
|
|
43
|
-
return
|
|
44
|
-
except Exception as e:
|
|
45
|
-
logger.error(f"Tool class {name} ({tool_name}) failed to provide argument_schema via get_argument_schema(): {e}. Skipping registration.", exc_info=True)
|
|
46
|
-
return
|
|
38
|
+
# Note: We do not call the schema methods here. We pass them as providers.
|
|
47
39
|
|
|
48
|
-
instantiation_config_schema: ParameterSchema = None
|
|
49
|
-
if hasattr(cls, 'get_config_schema'):
|
|
50
|
-
try:
|
|
51
|
-
instantiation_config_schema = cls.get_config_schema()
|
|
52
|
-
if instantiation_config_schema is not None and not isinstance(instantiation_config_schema, ParameterSchema):
|
|
53
|
-
logger.warning(f"Tool class {name} ({tool_name}) get_config_schema() returned non-ParameterSchema type: {type(instantiation_config_schema)}. Treating as no config schema.")
|
|
54
|
-
instantiation_config_schema = None
|
|
55
|
-
except Exception as e:
|
|
56
|
-
logger.warning(f"Tool class {name} ({tool_name}) has get_config_schema() but it failed: {e}. Assuming no instantiation config.")
|
|
57
|
-
|
|
58
40
|
# Get category from class attribute, defaulting to "General"
|
|
59
41
|
category_str = getattr(cls, 'CATEGORY', ToolCategory.GENERAL)
|
|
60
42
|
|
|
@@ -64,16 +46,14 @@ class ToolMeta(ABCMeta):
|
|
|
64
46
|
description=general_description,
|
|
65
47
|
tool_class=cls,
|
|
66
48
|
custom_factory=None,
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
argument_schema_provider=cls.get_argument_schema,
|
|
50
|
+
config_schema_provider=cls.get_config_schema,
|
|
69
51
|
origin=ToolOrigin.LOCAL,
|
|
70
52
|
category=category_str
|
|
71
53
|
)
|
|
72
54
|
default_tool_registry.register_tool(definition)
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
config_info = f"inst_config: {len(instantiation_config_schema) if instantiation_config_schema else '0'}"
|
|
76
|
-
logger.info(f"Auto-registered tool: '{tool_name}' from class {name} ({arg_schema_info}, {config_info})")
|
|
56
|
+
logger.info(f"Auto-registered tool: '{tool_name}' from class {name}")
|
|
77
57
|
|
|
78
58
|
except AttributeError as e:
|
|
79
59
|
logger.error(f"Tool class {name} is missing a required method ({e}). Skipping registration.")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/tools/usage/parsers/_string_decoders.py
|
|
2
|
+
"""Utility helpers for normalizing string content inside parsed tool payloads."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import html
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def decode_html_entities(data: Any) -> Any:
|
|
11
|
+
"""Recursively decode HTML/XML entities in strings within a data structure."""
|
|
12
|
+
if isinstance(data, dict):
|
|
13
|
+
return {key: decode_html_entities(value) for key, value in data.items()}
|
|
14
|
+
if isinstance(data, list):
|
|
15
|
+
return [decode_html_entities(item) for item in data]
|
|
16
|
+
if isinstance(data, str):
|
|
17
|
+
return html.unescape(data)
|
|
18
|
+
return data
|
|
@@ -3,6 +3,8 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import Dict, Any, TYPE_CHECKING, List
|
|
5
5
|
|
|
6
|
+
from ._string_decoders import decode_html_entities
|
|
7
|
+
|
|
6
8
|
from autobyteus.agent.tool_invocation import ToolInvocation
|
|
7
9
|
from .base_parser import BaseToolUsageParser
|
|
8
10
|
from .exceptions import ToolUsageParseException
|
|
@@ -53,9 +55,15 @@ class DefaultJsonToolUsageParser(BaseToolUsageParser):
|
|
|
53
55
|
logger.debug(f"Skipping tool block with invalid 'parameters' type ({type(arguments)}): {tool_block}")
|
|
54
56
|
continue
|
|
55
57
|
|
|
58
|
+
decoded_arguments = decode_html_entities(arguments)
|
|
59
|
+
decoded_tool_name = decode_html_entities(tool_name)
|
|
56
60
|
try:
|
|
57
61
|
# Pass id=None to trigger deterministic ID generation.
|
|
58
|
-
tool_invocation = ToolInvocation(
|
|
62
|
+
tool_invocation = ToolInvocation(
|
|
63
|
+
name=decoded_tool_name,
|
|
64
|
+
arguments=decoded_arguments,
|
|
65
|
+
id=None,
|
|
66
|
+
)
|
|
59
67
|
invocations.append(tool_invocation)
|
|
60
68
|
logger.info(f"Successfully parsed default JSON tool invocation for '{tool_name}'.")
|
|
61
69
|
except Exception as e:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
+
import html
|
|
3
4
|
from typing import TYPE_CHECKING, Dict, Any, List
|
|
4
5
|
from dataclasses import dataclass, field
|
|
5
6
|
|
|
@@ -124,6 +125,7 @@ class _XmlArgumentsParser:
|
|
|
124
125
|
|
|
125
126
|
final_args = context.stack[0]
|
|
126
127
|
self._cleanup_internal_keys(final_args)
|
|
128
|
+
final_args = self._decode_entities_inplace(final_args)
|
|
127
129
|
return final_args
|
|
128
130
|
|
|
129
131
|
def process_tag(self, tag_content: str, context: '_ParsingContext'):
|
|
@@ -213,6 +215,19 @@ class _XmlArgumentsParser:
|
|
|
213
215
|
for item in data:
|
|
214
216
|
self._cleanup_internal_keys(item)
|
|
215
217
|
|
|
218
|
+
def _decode_entities_inplace(self, data: Any):
|
|
219
|
+
if isinstance(data, dict):
|
|
220
|
+
for key, value in list(data.items()):
|
|
221
|
+
data[key] = self._decode_entities_inplace(value)
|
|
222
|
+
return data
|
|
223
|
+
if isinstance(data, list):
|
|
224
|
+
for index, item in enumerate(data):
|
|
225
|
+
data[index] = self._decode_entities_inplace(item)
|
|
226
|
+
return data
|
|
227
|
+
if isinstance(data, str):
|
|
228
|
+
return html.unescape(data)
|
|
229
|
+
return data
|
|
230
|
+
|
|
216
231
|
|
|
217
232
|
# --- Main Parser Class ---
|
|
218
233
|
|
|
@@ -299,4 +314,3 @@ class DefaultXmlToolUsageParser(BaseToolUsageParser):
|
|
|
299
314
|
"""
|
|
300
315
|
parser = _XmlArgumentsParser(xml_string)
|
|
301
316
|
return parser.parse()
|
|
302
|
-
|
|
@@ -7,6 +7,7 @@ from autobyteus.agent.tool_invocation import ToolInvocation
|
|
|
7
7
|
from .base_parser import BaseToolUsageParser
|
|
8
8
|
from .exceptions import ToolUsageParseException
|
|
9
9
|
from ._json_extractor import _find_json_blobs
|
|
10
|
+
from ._string_decoders import decode_html_entities
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from autobyteus.llm.utils.response_types import CompleteResponse
|
|
@@ -55,8 +56,10 @@ class GeminiJsonToolUsageParser(BaseToolUsageParser):
|
|
|
55
56
|
arguments = call_data.get("args")
|
|
56
57
|
|
|
57
58
|
if tool_name and isinstance(tool_name, str) and isinstance(arguments, dict):
|
|
59
|
+
decoded_tool_name = decode_html_entities(tool_name)
|
|
60
|
+
decoded_arguments = decode_html_entities(arguments)
|
|
58
61
|
# Pass id=None to trigger deterministic ID generation in ToolInvocation
|
|
59
|
-
tool_invocation = ToolInvocation(name=
|
|
62
|
+
tool_invocation = ToolInvocation(name=decoded_tool_name, arguments=decoded_arguments)
|
|
60
63
|
invocations.append(tool_invocation)
|
|
61
64
|
logger.info(f"Successfully parsed Gemini JSON tool invocation for '{tool_name}'.")
|
|
62
65
|
else:
|
|
@@ -7,6 +7,7 @@ from autobyteus.agent.tool_invocation import ToolInvocation
|
|
|
7
7
|
from .base_parser import BaseToolUsageParser
|
|
8
8
|
from .exceptions import ToolUsageParseException
|
|
9
9
|
from ._json_extractor import _find_json_blobs
|
|
10
|
+
from ._string_decoders import decode_html_entities
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from autobyteus.llm.utils.response_types import CompleteResponse
|
|
@@ -138,7 +139,9 @@ class OpenAiJsonToolUsageParser(BaseToolUsageParser):
|
|
|
138
139
|
|
|
139
140
|
try:
|
|
140
141
|
# The ToolInvocation constructor will generate a deterministic ID if 'id' is None.
|
|
141
|
-
|
|
142
|
+
decoded_tool_name = decode_html_entities(tool_name)
|
|
143
|
+
decoded_arguments = decode_html_entities(arguments)
|
|
144
|
+
tool_invocation = ToolInvocation(name=decoded_tool_name, arguments=decoded_arguments, id=None)
|
|
142
145
|
logger.info(f"Successfully parsed OpenAI-style JSON tool invocation for '{tool_name}'.")
|
|
143
146
|
return tool_invocation
|
|
144
147
|
except Exception as e:
|
|
@@ -91,7 +91,7 @@ class CoordinatorPromptPreparationStep(BaseWorkflowBootstrapStep):
|
|
|
91
91
|
|
|
92
92
|
prompt_parts.append(tools_section)
|
|
93
93
|
|
|
94
|
-
final_instruction = "### Your Task\nAnalyze the user's request, formulate a plan, and use the `
|
|
94
|
+
final_instruction = "### Your Task\nAnalyze the user's request, formulate a plan, and use the `send_message_to` tool to delegate tasks to your team. Address team members by their unique ID as listed under 'Your Team'."
|
|
95
95
|
prompt_parts.append(final_instruction)
|
|
96
96
|
else:
|
|
97
97
|
role_and_goal = (
|
|
@@ -105,4 +105,3 @@ class CoordinatorPromptPreparationStep(BaseWorkflowBootstrapStep):
|
|
|
105
105
|
prompt_parts.append(final_instruction)
|
|
106
106
|
|
|
107
107
|
return "\n\n".join(prompt_parts)
|
|
108
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autobyteus
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Multi-Agent framework
|
|
5
5
|
Home-page: https://github.com/AutoByteus/autobyteus
|
|
6
6
|
Author: Ryan Zheng
|
|
@@ -16,14 +16,15 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: aiohttp
|
|
18
18
|
Requires-Dist: anthropic
|
|
19
|
-
Requires-Dist: autobyteus-llm-client==1.1.3
|
|
20
19
|
Requires-Dist: beautifulsoup4
|
|
21
20
|
Requires-Dist: boto3
|
|
22
21
|
Requires-Dist: botocore
|
|
23
22
|
Requires-Dist: brui-core==1.0.9
|
|
24
23
|
Requires-Dist: certifi==2025.4.26
|
|
24
|
+
Requires-Dist: cryptography
|
|
25
25
|
Requires-Dist: google-api-python-client
|
|
26
26
|
Requires-Dist: google-genai==1.38.0
|
|
27
|
+
Requires-Dist: httpx
|
|
27
28
|
Requires-Dist: Jinja2
|
|
28
29
|
Requires-Dist: mcp[cli]
|
|
29
30
|
Requires-Dist: mistral_common
|
|
@@ -98,11 +99,11 @@ Launch and monitor your agent teams with our built-in Textual-based TUI.
|
|
|
98
99
|
- **Hierarchical View**: See the structure of your team, including sub-teams and their agents.
|
|
99
100
|
- **Real-Time Status**: Agent and team statuses are updated live, showing you who is idle, thinking, or executing a tool.
|
|
100
101
|
- **Detailed Logs**: Select any agent to view a detailed, streaming log of their thoughts, actions, and tool interactions.
|
|
101
|
-
- **Live Task
|
|
102
|
+
- **Live Task Plan**: Watch your team's `TaskPlan` update in real-time as the coordinator publishes a plan and agents complete their tasks.
|
|
102
103
|
|
|
103
|
-
| TUI - Detailed Agent Log | TUI - Task
|
|
104
|
+
| TUI - Detailed Agent Log | TUI - Task Plan with Completed Task |
|
|
104
105
|
| :---: | :---: |
|
|
105
|
-
|  |  |  |
|
|
106
107
|
|
|
107
108
|
#### 🏗️ Fluent Team Building
|
|
108
109
|
Define complex agent and team structures with an intuitive, fluent API. The `AgentTeamBuilder` makes composing your team simple and readable.
|
|
@@ -129,7 +130,7 @@ Autobyteus intelligently handles tool communication with LLMs while giving you f
|
|
|
129
130
|
#### 📈 Flexible Communication Protocols
|
|
130
131
|
Choose the collaboration pattern that best fits your use case with configurable `TaskNotificationMode`s.
|
|
131
132
|
- **`AGENT_MANUAL_NOTIFICATION` (Default)**: A traditional approach where a coordinator agent is responsible for creating a plan and then explicitly notifying other agents to begin their work via messages.
|
|
132
|
-
- **`SYSTEM_EVENT_DRIVEN`**: A more automated approach where the coordinator's only job is to publish a plan to the `
|
|
133
|
+
- **`SYSTEM_EVENT_DRIVEN`**: A more automated approach where the coordinator's only job is to publish a plan to the `TaskPlan`. The framework then monitors the board and automatically notifies agents when their tasks become unblocked, enabling parallel execution and reducing coordinator overhead.
|
|
133
134
|
|
|
134
135
|
## Requirements
|
|
135
136
|
|