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.
- autobyteus/agent/context/__init__.py +4 -2
- autobyteus/agent/context/agent_config.py +0 -4
- autobyteus/agent/context/agent_context_registry.py +73 -0
- autobyteus/agent/events/notifiers.py +4 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
- autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
- autobyteus/agent/message/send_message_to.py +29 -23
- autobyteus/agent/runtime/agent_runtime.py +10 -2
- autobyteus/agent/sender_type.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +6 -0
- autobyteus/agent/streaming/stream_event_payloads.py +12 -0
- autobyteus/agent/streaming/stream_events.py +3 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
- autobyteus/agent_team/__init__.py +1 -0
- autobyteus/agent_team/agent_team.py +93 -0
- autobyteus/agent_team/agent_team_builder.py +184 -0
- autobyteus/agent_team/base_agent_team.py +86 -0
- autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
- autobyteus/agent_team/context/__init__.py +17 -0
- autobyteus/agent_team/context/agent_team_config.py +33 -0
- autobyteus/agent_team/context/agent_team_context.py +61 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
- autobyteus/agent_team/context/team_manager.py +147 -0
- autobyteus/agent_team/context/team_node_config.py +76 -0
- autobyteus/agent_team/events/__init__.py +29 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
- autobyteus/agent_team/events/agent_team_events.py +53 -0
- autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
- autobyteus/agent_team/exceptions.py +8 -0
- autobyteus/agent_team/factory/__init__.py +9 -0
- autobyteus/agent_team/factory/agent_team_factory.py +99 -0
- autobyteus/agent_team/handlers/__init__.py +19 -0
- autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
- autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
- autobyteus/agent_team/phases/__init__.py +11 -0
- autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
- autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
- autobyteus/agent_team/runtime/__init__.py +13 -0
- autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
- autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
- autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
- autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
- autobyteus/agent_team/streaming/__init__.py +26 -0
- autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
- autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
- autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
- autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
- autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
- autobyteus/agent_team/task_notification/__init__.py +11 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
- autobyteus/agent_team/utils/__init__.py +9 -0
- autobyteus/agent_team/utils/wait_for_idle.py +46 -0
- autobyteus/cli/agent_team_tui/__init__.py +4 -0
- autobyteus/cli/agent_team_tui/app.py +210 -0
- autobyteus/cli/agent_team_tui/state.py +180 -0
- autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
- autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
- autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
- autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
- autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
- autobyteus/events/event_types.py +7 -2
- autobyteus/llm/api/autobyteus_llm.py +11 -12
- autobyteus/llm/api/lmstudio_llm.py +10 -13
- autobyteus/llm/api/ollama_llm.py +8 -13
- autobyteus/llm/autobyteus_provider.py +73 -46
- autobyteus/llm/llm_factory.py +102 -140
- autobyteus/llm/lmstudio_provider.py +63 -48
- autobyteus/llm/models.py +83 -53
- autobyteus/llm/ollama_provider.py +69 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +13 -13
- autobyteus/llm/runtimes.py +11 -0
- autobyteus/task_management/__init__.py +43 -0
- autobyteus/task_management/base_task_board.py +68 -0
- autobyteus/task_management/converters/__init__.py +11 -0
- autobyteus/task_management/converters/task_board_converter.py +64 -0
- autobyteus/task_management/converters/task_plan_converter.py +48 -0
- autobyteus/task_management/deliverable.py +16 -0
- autobyteus/task_management/deliverables/__init__.py +8 -0
- autobyteus/task_management/deliverables/file_deliverable.py +15 -0
- autobyteus/task_management/events.py +27 -0
- autobyteus/task_management/in_memory_task_board.py +126 -0
- autobyteus/task_management/schemas/__init__.py +15 -0
- autobyteus/task_management/schemas/deliverable_schema.py +13 -0
- autobyteus/task_management/schemas/plan_definition.py +35 -0
- autobyteus/task_management/schemas/task_status_report.py +27 -0
- autobyteus/task_management/task_plan.py +110 -0
- autobyteus/task_management/tools/__init__.py +14 -0
- autobyteus/task_management/tools/get_task_board_status.py +68 -0
- autobyteus/task_management/tools/publish_task_plan.py +113 -0
- autobyteus/task_management/tools/update_task_status.py +135 -0
- autobyteus/tools/bash/bash_executor.py +59 -14
- autobyteus/tools/mcp/config_service.py +63 -58
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server_instance_manager.py +30 -4
- autobyteus/tools/mcp/tool_registrar.py +103 -50
- autobyteus/tools/parameter_schema.py +17 -11
- autobyteus/tools/registry/tool_definition.py +24 -29
- autobyteus/tools/tool_category.py +1 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
- autobyteus/tools/usage/providers/__init__.py +2 -12
- autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
- autobyteus/tools/usage/registries/__init__.py +7 -12
- autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
- autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
- examples/agent_team/__init__.py +1 -0
- examples/run_browser_agent.py +17 -15
- examples/run_google_slides_agent.py +17 -16
- examples/run_poem_writer.py +22 -12
- examples/run_sqlite_agent.py +17 -15
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
- autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
- autobyteus/tools/usage/providers/json_example_provider.py +0 -32
- autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
- examples/workflow/__init__.py +0 -1
- examples/workflow/run_basic_research_workflow.py +0 -189
- examples/workflow/run_code_review_workflow.py +0 -269
- examples/workflow/run_debate_workflow.py +0 -212
- examples/workflow/run_workflow_with_tui.py +0 -153
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the sidebar widget that lists all nodes in the team hierarchy.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Dict, Any, Optional
|
|
7
|
+
|
|
8
|
+
from textual.message import Message
|
|
9
|
+
from textual.widgets import Static, Tree
|
|
10
|
+
from textual.widgets.tree import TreeNode
|
|
11
|
+
from textual.containers import Vertical
|
|
12
|
+
|
|
13
|
+
from autobyteus.agent.phases import AgentOperationalPhase
|
|
14
|
+
from autobyteus.agent_team.phases import AgentTeamOperationalPhase
|
|
15
|
+
from .shared import (
|
|
16
|
+
AGENT_PHASE_ICONS, TEAM_PHASE_ICONS, SUB_TEAM_ICON,
|
|
17
|
+
TEAM_ICON, SPEAKING_ICON, DEFAULT_ICON
|
|
18
|
+
)
|
|
19
|
+
from .logo import Logo
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
class AgentListSidebar(Static):
|
|
24
|
+
"""A widget to display the hierarchical list of team nodes. This is a dumb
|
|
25
|
+
rendering component driven by the TUIStateStore."""
|
|
26
|
+
|
|
27
|
+
class NodeSelected(Message):
|
|
28
|
+
"""Posted when any node is selected in the tree."""
|
|
29
|
+
def __init__(self, node_data: Dict[str, Any]) -> None:
|
|
30
|
+
self.node_data = node_data
|
|
31
|
+
super().__init__()
|
|
32
|
+
|
|
33
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
34
|
+
super().__init__(*args, **kwargs)
|
|
35
|
+
self._node_map: Dict[str, TreeNode] = {} # Maps node names to TreeNode objects
|
|
36
|
+
|
|
37
|
+
def compose(self):
|
|
38
|
+
with Vertical():
|
|
39
|
+
yield Tree("Agent Team", id="agent-tree")
|
|
40
|
+
yield Logo()
|
|
41
|
+
|
|
42
|
+
def on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
|
|
43
|
+
"""Handle node selection from the tree."""
|
|
44
|
+
if event.node.data:
|
|
45
|
+
self.post_message(self.NodeSelected(event.node.data))
|
|
46
|
+
event.stop()
|
|
47
|
+
|
|
48
|
+
def _build_label(self, name: str, node_data: Dict, agent_phases: Dict, team_phases: Dict, speaking_agents: Dict) -> str:
|
|
49
|
+
"""Constructs the display label for a tree node."""
|
|
50
|
+
node_type = node_data["type"]
|
|
51
|
+
icon = DEFAULT_ICON
|
|
52
|
+
|
|
53
|
+
if node_type == "agent":
|
|
54
|
+
phase = agent_phases.get(name, AgentOperationalPhase.UNINITIALIZED)
|
|
55
|
+
icon = SPEAKING_ICON if speaking_agents.get(name) else AGENT_PHASE_ICONS.get(phase, DEFAULT_ICON)
|
|
56
|
+
label = f"{icon} {name}"
|
|
57
|
+
elif node_type in ["team", "subteam"]:
|
|
58
|
+
phase = team_phases.get(name, AgentTeamOperationalPhase.UNINITIALIZED)
|
|
59
|
+
default_icon = TEAM_ICON if node_type == "team" else SUB_TEAM_ICON
|
|
60
|
+
icon = TEAM_PHASE_ICONS.get(phase, default_icon)
|
|
61
|
+
role = node_data.get("role")
|
|
62
|
+
label = f"{icon} {role or name}"
|
|
63
|
+
if role and role != name:
|
|
64
|
+
label += f" ({name})"
|
|
65
|
+
else:
|
|
66
|
+
label = f"{icon} {name}"
|
|
67
|
+
|
|
68
|
+
return label
|
|
69
|
+
|
|
70
|
+
def update_tree(self, tree_data: Dict, agent_phases: Dict[str, AgentOperationalPhase], team_phases: Dict[str, AgentTeamOperationalPhase], speaking_agents: Dict[str, bool]):
|
|
71
|
+
"""
|
|
72
|
+
Performs an in-place update of the tree to reflect the new state,
|
|
73
|
+
avoiding a full rebuild for better performance and preserving UI state like expansion.
|
|
74
|
+
"""
|
|
75
|
+
tree = self.query_one(Tree)
|
|
76
|
+
|
|
77
|
+
if not tree_data:
|
|
78
|
+
tree.root.set_label("Initializing agent team...")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
root_name = list(tree_data.keys())[0]
|
|
82
|
+
root_node_data = tree_data[root_name]
|
|
83
|
+
|
|
84
|
+
# Kick off the recursive update from the root.
|
|
85
|
+
self._update_node_recursively(tree.root, root_node_data, agent_phases, team_phases, speaking_agents)
|
|
86
|
+
|
|
87
|
+
# Ensure the root is expanded on the first run.
|
|
88
|
+
if not tree.root.is_expanded:
|
|
89
|
+
tree.root.expand()
|
|
90
|
+
|
|
91
|
+
def _update_node_recursively(self, ui_node: TreeNode, node_data: Dict, agent_phases: Dict, team_phases: Dict, speaking_agents: Dict):
|
|
92
|
+
"""Recursively updates a node and reconciles its children."""
|
|
93
|
+
# 1. Update the current node's label and data
|
|
94
|
+
name = node_data['name']
|
|
95
|
+
label = self._build_label(name, node_data, agent_phases, team_phases, speaking_agents)
|
|
96
|
+
ui_node.set_label(label)
|
|
97
|
+
ui_node.data = node_data
|
|
98
|
+
self._node_map[name] = ui_node # Ensure map is always up-to-date
|
|
99
|
+
|
|
100
|
+
# 2. Reconcile children
|
|
101
|
+
new_children_data = node_data.get("children", {})
|
|
102
|
+
existing_ui_children_by_name = {child.data['name']: child for child in ui_node.children if child.data}
|
|
103
|
+
|
|
104
|
+
# Add new nodes and update existing ones
|
|
105
|
+
for child_name, child_data in new_children_data.items():
|
|
106
|
+
if child_name in existing_ui_children_by_name:
|
|
107
|
+
# Node exists, so we recursively update it
|
|
108
|
+
child_ui_node = existing_ui_children_by_name[child_name]
|
|
109
|
+
self._update_node_recursively(child_ui_node, child_data, agent_phases, team_phases, speaking_agents)
|
|
110
|
+
else:
|
|
111
|
+
# Node is new, so we add it
|
|
112
|
+
new_child_label = self._build_label(child_name, child_data, agent_phases, team_phases, speaking_agents)
|
|
113
|
+
is_leaf = child_data.get("children", {}) == {} and child_data['type'] == 'agent'
|
|
114
|
+
|
|
115
|
+
if is_leaf:
|
|
116
|
+
new_ui_node = ui_node.add_leaf(new_child_label, data=child_data)
|
|
117
|
+
else:
|
|
118
|
+
new_ui_node = ui_node.add(new_child_label, data=child_data)
|
|
119
|
+
# Since this is a new branch, we must build its children too
|
|
120
|
+
self._update_node_recursively(new_ui_node, child_data, agent_phases, team_phases, speaking_agents)
|
|
121
|
+
|
|
122
|
+
self._node_map[child_name] = new_ui_node
|
|
123
|
+
|
|
124
|
+
# Remove old nodes that no longer exist in the new data
|
|
125
|
+
nodes_to_remove = []
|
|
126
|
+
for existing_child_name, existing_child_node in existing_ui_children_by_name.items():
|
|
127
|
+
if existing_child_name not in new_children_data:
|
|
128
|
+
nodes_to_remove.append(existing_child_node)
|
|
129
|
+
if existing_child_name in self._node_map:
|
|
130
|
+
del self._node_map[existing_child_name]
|
|
131
|
+
|
|
132
|
+
for node in nodes_to_remove:
|
|
133
|
+
node.remove()
|
|
134
|
+
|
|
135
|
+
def update_selection(self, node_name: Optional[str]):
|
|
136
|
+
"""Updates the tree's selection and expands parents to make it visible."""
|
|
137
|
+
if not node_name or node_name not in self._node_map:
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
tree = self.query_one(Tree)
|
|
141
|
+
node_to_select = self._node_map[node_name]
|
|
142
|
+
|
|
143
|
+
parent = node_to_select.parent
|
|
144
|
+
while parent:
|
|
145
|
+
parent.expand()
|
|
146
|
+
parent = parent.parent
|
|
147
|
+
|
|
148
|
+
tree.select_node(node_to_select)
|
|
149
|
+
tree.scroll_to_node(node_to_select)
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines the main focus pane widget for displaying detailed logs or summaries.
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
from typing import Optional, List, Any, Dict
|
|
7
|
+
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.syntax import Syntax
|
|
11
|
+
from textual.message import Message
|
|
12
|
+
from textual.widgets import Input, Static, Button
|
|
13
|
+
from textual.containers import VerticalScroll, Horizontal
|
|
14
|
+
|
|
15
|
+
from autobyteus.agent.phases import AgentOperationalPhase
|
|
16
|
+
from autobyteus.agent_team.phases import AgentTeamOperationalPhase
|
|
17
|
+
from autobyteus.task_management.base_task_board import TaskStatus
|
|
18
|
+
from autobyteus.task_management.task_plan import Task
|
|
19
|
+
from autobyteus.agent.streaming.stream_events import StreamEvent as AgentStreamEvent, StreamEventType as AgentStreamEventType
|
|
20
|
+
from autobyteus.agent.streaming.stream_event_payloads import (
|
|
21
|
+
AgentOperationalPhaseTransitionData, AssistantChunkData, AssistantCompleteResponseData,
|
|
22
|
+
ErrorEventData, ToolInteractionLogEntryData, ToolInvocationApprovalRequestedData, ToolInvocationAutoExecutingData,
|
|
23
|
+
SystemTaskNotificationData
|
|
24
|
+
)
|
|
25
|
+
from .shared import (
|
|
26
|
+
AGENT_PHASE_ICONS, TEAM_PHASE_ICONS, SUB_TEAM_ICON, DEFAULT_ICON,
|
|
27
|
+
USER_ICON, ASSISTANT_ICON, TEAM_ICON, AGENT_ICON, SYSTEM_TASK_ICON
|
|
28
|
+
)
|
|
29
|
+
from . import renderables
|
|
30
|
+
from .task_board_panel import TaskBoardPanel
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
class FocusPane(Static):
|
|
35
|
+
"""
|
|
36
|
+
A widget to display detailed logs for agents or high-level dashboards for teams.
|
|
37
|
+
This is a dumb rendering component driven by the TUIStateStore.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
class MessageSubmitted(Message):
|
|
41
|
+
def __init__(self, text: str, agent_name: str) -> None:
|
|
42
|
+
self.text = text
|
|
43
|
+
self.agent_name = agent_name
|
|
44
|
+
super().__init__()
|
|
45
|
+
|
|
46
|
+
class ApprovalSubmitted(Message):
|
|
47
|
+
def __init__(self, agent_name: str, invocation_id: str, is_approved: bool, reason: Optional[str]) -> None:
|
|
48
|
+
self.agent_name = agent_name
|
|
49
|
+
self.invocation_id = invocation_id
|
|
50
|
+
self.is_approved = is_approved
|
|
51
|
+
self.reason = reason
|
|
52
|
+
super().__init__()
|
|
53
|
+
|
|
54
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
55
|
+
super().__init__(*args, **kwargs)
|
|
56
|
+
self._focused_node_data: Optional[Dict[str, Any]] = None
|
|
57
|
+
self._pending_approval_data: Optional[ToolInvocationApprovalRequestedData] = None
|
|
58
|
+
|
|
59
|
+
# State variables for streaming
|
|
60
|
+
self._thinking_widget: Optional[Static] = None
|
|
61
|
+
self._thinking_text: Optional[Text] = None
|
|
62
|
+
self._assistant_content_widget: Optional[Static] = None
|
|
63
|
+
self._assistant_content_text: Optional[Text] = None
|
|
64
|
+
|
|
65
|
+
# Buffers for batched UI updates to improve performance
|
|
66
|
+
self._reasoning_buffer: str = ""
|
|
67
|
+
self._content_buffer: str = ""
|
|
68
|
+
|
|
69
|
+
def compose(self):
|
|
70
|
+
yield Static("Select a node from the sidebar", id="focus-pane-title")
|
|
71
|
+
yield VerticalScroll(id="focus-pane-log-container")
|
|
72
|
+
yield Horizontal(id="approval-buttons")
|
|
73
|
+
yield Input(placeholder="Select an agent to send messages...", id="focus-pane-input", disabled=True)
|
|
74
|
+
|
|
75
|
+
async def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
76
|
+
if event.value and self._focused_node_data and self._focused_node_data.get("type") == 'agent':
|
|
77
|
+
log_container = self.query_one("#focus-pane-log-container")
|
|
78
|
+
user_message_text = Text(f"{USER_ICON} You: {event.value}", style="bright_blue")
|
|
79
|
+
await log_container.mount(Static(""))
|
|
80
|
+
await log_container.mount(Static(user_message_text))
|
|
81
|
+
log_container.scroll_end(animate=False)
|
|
82
|
+
|
|
83
|
+
self.post_message(self.MessageSubmitted(event.value, self._focused_node_data['name']))
|
|
84
|
+
self.query_one(Input).clear()
|
|
85
|
+
event.stop()
|
|
86
|
+
|
|
87
|
+
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
88
|
+
if not self._pending_approval_data or not self._focused_node_data:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
is_approved = event.button.id == "approve-btn"
|
|
92
|
+
reason = "User approved via TUI." if is_approved else "User denied via TUI."
|
|
93
|
+
|
|
94
|
+
log_container = self.query_one("#focus-pane-log-container")
|
|
95
|
+
approval_text = "APPROVED" if is_approved else "DENIED"
|
|
96
|
+
display_text = Text(f"{USER_ICON} You: {approval_text} (Reason: {reason})", style="bright_cyan")
|
|
97
|
+
await log_container.mount(Static(""))
|
|
98
|
+
await log_container.mount(Static(display_text))
|
|
99
|
+
log_container.scroll_end(animate=False)
|
|
100
|
+
|
|
101
|
+
self.post_message(self.ApprovalSubmitted(
|
|
102
|
+
agent_name=self._focused_node_data['name'],
|
|
103
|
+
invocation_id=self._pending_approval_data.invocation_id,
|
|
104
|
+
is_approved=is_approved, reason=reason
|
|
105
|
+
))
|
|
106
|
+
await self._clear_approval_ui()
|
|
107
|
+
event.stop()
|
|
108
|
+
|
|
109
|
+
async def _clear_approval_ui(self):
|
|
110
|
+
self._pending_approval_data = None
|
|
111
|
+
await self.query_one("#approval-buttons").remove_children()
|
|
112
|
+
input_widget = self.query_one(Input)
|
|
113
|
+
if self._focused_node_data and self._focused_node_data.get("type") == "agent":
|
|
114
|
+
input_widget.disabled = False
|
|
115
|
+
input_widget.placeholder = f"Send a message to {self._focused_node_data['name']}..."
|
|
116
|
+
input_widget.focus()
|
|
117
|
+
else:
|
|
118
|
+
input_widget.disabled = True
|
|
119
|
+
input_widget.placeholder = "Select an agent to send messages..."
|
|
120
|
+
|
|
121
|
+
async def _show_approval_prompt(self):
|
|
122
|
+
if not self._pending_approval_data: return
|
|
123
|
+
input_widget = self.query_one(Input)
|
|
124
|
+
input_widget.placeholder = "Please approve or deny the tool call..."
|
|
125
|
+
input_widget.disabled = True
|
|
126
|
+
button_container = self.query_one("#approval-buttons")
|
|
127
|
+
await button_container.remove_children()
|
|
128
|
+
await button_container.mount(
|
|
129
|
+
Button("Approve", variant="success", id="approve-btn"),
|
|
130
|
+
Button("Deny", variant="error", id="deny-btn")
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def _update_title(self, agent_phases: Dict[str, AgentOperationalPhase], team_phases: Dict[str, AgentTeamOperationalPhase]):
|
|
134
|
+
"""Renders the title of the focus pane with the node's current status."""
|
|
135
|
+
if not self._focused_node_data:
|
|
136
|
+
self.query_one("#focus-pane-title").update("Select a node from the sidebar")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
node_name = self._focused_node_data.get("name", "Unknown")
|
|
140
|
+
node_type = self._focused_node_data.get("type", "node")
|
|
141
|
+
node_type_str = node_type.replace("_", " ").capitalize()
|
|
142
|
+
|
|
143
|
+
title_icon = DEFAULT_ICON
|
|
144
|
+
phase_str = ""
|
|
145
|
+
|
|
146
|
+
if node_type == 'agent':
|
|
147
|
+
title_icon = AGENT_ICON
|
|
148
|
+
phase = agent_phases.get(node_name, AgentOperationalPhase.UNINITIALIZED)
|
|
149
|
+
phase_str = f" (Status: {phase.value})"
|
|
150
|
+
elif node_type == 'subteam':
|
|
151
|
+
title_icon = SUB_TEAM_ICON
|
|
152
|
+
phase = team_phases.get(node_name, AgentTeamOperationalPhase.UNINITIALIZED)
|
|
153
|
+
phase_str = f" (Status: {phase.value})"
|
|
154
|
+
elif node_type == 'team':
|
|
155
|
+
title_icon = TEAM_ICON
|
|
156
|
+
phase = team_phases.get(node_name, AgentTeamOperationalPhase.UNINITIALIZED)
|
|
157
|
+
phase_str = f" (Status: {phase.value})"
|
|
158
|
+
|
|
159
|
+
self.query_one("#focus-pane-title").update(f"{title_icon} {node_type_str}: [bold]{node_name}[/bold]{phase_str}")
|
|
160
|
+
|
|
161
|
+
def update_current_node_status(self, all_agent_phases: Dict, all_team_phases: Dict):
|
|
162
|
+
"""A lightweight method to only update the title with the latest status."""
|
|
163
|
+
self._update_title(all_agent_phases, all_team_phases)
|
|
164
|
+
|
|
165
|
+
async def update_content(self, node_data: Dict[str, Any], history: List[Any],
|
|
166
|
+
pending_approval: Optional[ToolInvocationApprovalRequestedData],
|
|
167
|
+
all_agent_phases: Dict[str, AgentOperationalPhase],
|
|
168
|
+
all_team_phases: Dict[str, AgentTeamOperationalPhase],
|
|
169
|
+
task_plan: Optional[List[Task]],
|
|
170
|
+
task_statuses: Optional[Dict[str, TaskStatus]]):
|
|
171
|
+
"""The main method to update the entire pane based on new state."""
|
|
172
|
+
self.flush_stream_buffers()
|
|
173
|
+
|
|
174
|
+
self._focused_node_data = node_data
|
|
175
|
+
self._pending_approval_data = pending_approval
|
|
176
|
+
|
|
177
|
+
self._update_title(all_agent_phases, all_team_phases)
|
|
178
|
+
|
|
179
|
+
log_container = self.query_one("#focus-pane-log-container")
|
|
180
|
+
await log_container.remove_children()
|
|
181
|
+
|
|
182
|
+
self._thinking_widget = None
|
|
183
|
+
self._thinking_text = None
|
|
184
|
+
self._assistant_content_widget = None
|
|
185
|
+
self._assistant_content_text = None
|
|
186
|
+
|
|
187
|
+
await self._clear_approval_ui()
|
|
188
|
+
|
|
189
|
+
if self._focused_node_data.get("type") == 'agent':
|
|
190
|
+
for event in history:
|
|
191
|
+
await self.add_agent_event(event)
|
|
192
|
+
if self._pending_approval_data:
|
|
193
|
+
await self._show_approval_prompt()
|
|
194
|
+
elif self._focused_node_data.get("type") in ['team', 'subteam']:
|
|
195
|
+
await self._render_team_dashboard(node_data, all_agent_phases, all_team_phases, task_plan, task_statuses)
|
|
196
|
+
|
|
197
|
+
async def _render_team_dashboard(self, node_data: Dict[str, Any],
|
|
198
|
+
all_agent_phases: Dict[str, AgentOperationalPhase],
|
|
199
|
+
all_team_phases: Dict[str, AgentTeamOperationalPhase],
|
|
200
|
+
task_plan: Optional[List[Task]],
|
|
201
|
+
task_statuses: Optional[Dict[str, TaskStatus]]):
|
|
202
|
+
"""Renders a static summary dashboard for a team or sub-team."""
|
|
203
|
+
log_container = self.query_one("#focus-pane-log-container")
|
|
204
|
+
|
|
205
|
+
phase = all_team_phases.get(node_data['name'], AgentTeamOperationalPhase.UNINITIALIZED)
|
|
206
|
+
phase_icon = TEAM_PHASE_ICONS.get(phase, DEFAULT_ICON)
|
|
207
|
+
info_text = Text()
|
|
208
|
+
info_text.append(f"Name: {node_data['name']}\n", style="bold")
|
|
209
|
+
if node_data.get('role'):
|
|
210
|
+
info_text.append(f"Role: {node_data['role']}\n")
|
|
211
|
+
info_text.append(f"Status: {phase_icon} {phase.value}")
|
|
212
|
+
await log_container.mount(Static(Panel(info_text, title="Team Info", border_style="green", title_align="left")))
|
|
213
|
+
|
|
214
|
+
await log_container.mount(TaskBoardPanel(tasks=task_plan, statuses=task_statuses, team_name=node_data['name']))
|
|
215
|
+
|
|
216
|
+
children_data = node_data.get("children", {})
|
|
217
|
+
if children_data:
|
|
218
|
+
team_text = Text()
|
|
219
|
+
for name, child_node in children_data.items():
|
|
220
|
+
if child_node['type'] == 'agent':
|
|
221
|
+
agent_phase = all_agent_phases.get(name, AgentOperationalPhase.UNINITIALIZED)
|
|
222
|
+
agent_icon = AGENT_PHASE_ICONS.get(agent_phase, DEFAULT_ICON)
|
|
223
|
+
team_text.append(f" ▪ {agent_icon} {name} (Agent): {agent_phase.value}\n")
|
|
224
|
+
elif child_node['type'] == 'subteam':
|
|
225
|
+
wf_phase = all_team_phases.get(name, AgentTeamOperationalPhase.UNINITIALIZED)
|
|
226
|
+
wf_icon = TEAM_PHASE_ICONS.get(wf_phase, SUB_TEAM_ICON)
|
|
227
|
+
team_text.append(f" ▪ {wf_icon} {name} (Sub-Team): {wf_phase.value}\n")
|
|
228
|
+
await log_container.mount(Static(Panel(team_text, title="Team Status", border_style="blue", title_align="left")))
|
|
229
|
+
|
|
230
|
+
async def _close_thinking_block(self, scroll: bool = True):
|
|
231
|
+
if self._thinking_widget and self._thinking_text:
|
|
232
|
+
self.flush_stream_buffers()
|
|
233
|
+
self._thinking_text.append("\n</Thinking>", style="dim italic cyan")
|
|
234
|
+
self._thinking_widget.update(self._thinking_text)
|
|
235
|
+
if scroll:
|
|
236
|
+
self.query_one("#focus-pane-log-container").scroll_end(animate=False)
|
|
237
|
+
self._thinking_widget = None
|
|
238
|
+
self._thinking_text = None
|
|
239
|
+
|
|
240
|
+
def flush_stream_buffers(self):
|
|
241
|
+
scrolled = False
|
|
242
|
+
if self._reasoning_buffer and self._thinking_widget and self._thinking_text:
|
|
243
|
+
self._thinking_text.append(self._reasoning_buffer)
|
|
244
|
+
self._thinking_widget.update(self._thinking_text)
|
|
245
|
+
self._reasoning_buffer = ""
|
|
246
|
+
scrolled = True
|
|
247
|
+
if self._content_buffer and self._assistant_content_widget and self._assistant_content_text:
|
|
248
|
+
self._assistant_content_text.append(self._content_buffer)
|
|
249
|
+
self._assistant_content_widget.update(self._assistant_content_text)
|
|
250
|
+
self._content_buffer = ""
|
|
251
|
+
scrolled = True
|
|
252
|
+
if scrolled:
|
|
253
|
+
self.query_one("#focus-pane-log-container").scroll_end(animate=False)
|
|
254
|
+
|
|
255
|
+
async def add_agent_event(self, event: AgentStreamEvent):
|
|
256
|
+
log_container = self.query_one("#focus-pane-log-container")
|
|
257
|
+
event_type = event.event_type
|
|
258
|
+
|
|
259
|
+
if event_type == AgentStreamEventType.ASSISTANT_CHUNK:
|
|
260
|
+
data: AssistantChunkData = event.data
|
|
261
|
+
if data.reasoning:
|
|
262
|
+
if self._thinking_widget is None:
|
|
263
|
+
self.flush_stream_buffers()
|
|
264
|
+
await log_container.mount(Static(""))
|
|
265
|
+
self._thinking_text = Text("<Thinking>\n", style="dim italic cyan")
|
|
266
|
+
self._thinking_widget = Static(self._thinking_text)
|
|
267
|
+
await log_container.mount(self._thinking_widget)
|
|
268
|
+
self._reasoning_buffer += data.reasoning
|
|
269
|
+
if data.content:
|
|
270
|
+
if self._thinking_widget: await self._close_thinking_block()
|
|
271
|
+
if self._assistant_content_widget is None:
|
|
272
|
+
await log_container.mount(Static(""))
|
|
273
|
+
self._assistant_content_text = Text(f"{ASSISTANT_ICON} assistant: ", style="bold green")
|
|
274
|
+
self._assistant_content_widget = Static(self._assistant_content_text)
|
|
275
|
+
await log_container.mount(self._assistant_content_widget)
|
|
276
|
+
self._content_buffer += data.content
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
if event_type == AgentStreamEventType.ASSISTANT_COMPLETE_RESPONSE:
|
|
280
|
+
was_streaming_content = self._assistant_content_widget is not None
|
|
281
|
+
self.flush_stream_buffers()
|
|
282
|
+
await self._close_thinking_block()
|
|
283
|
+
self._assistant_content_widget = None
|
|
284
|
+
self._assistant_content_text = None
|
|
285
|
+
if not was_streaming_content:
|
|
286
|
+
renderables_list = renderables.render_assistant_complete_response(event.data)
|
|
287
|
+
if renderables_list:
|
|
288
|
+
await log_container.mount(Static(""))
|
|
289
|
+
for item in renderables_list: await log_container.mount(Static(item))
|
|
290
|
+
log_container.scroll_end(animate=False)
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
is_stream_breaking_event = event_type in [
|
|
294
|
+
AgentStreamEventType.TOOL_INTERACTION_LOG_ENTRY,
|
|
295
|
+
AgentStreamEventType.TOOL_INVOCATION_AUTO_EXECUTING,
|
|
296
|
+
AgentStreamEventType.TOOL_INVOCATION_APPROVAL_REQUESTED,
|
|
297
|
+
AgentStreamEventType.ERROR_EVENT,
|
|
298
|
+
AgentStreamEventType.SYSTEM_TASK_NOTIFICATION, # NEW
|
|
299
|
+
]
|
|
300
|
+
if is_stream_breaking_event:
|
|
301
|
+
self.flush_stream_buffers()
|
|
302
|
+
await self._close_thinking_block()
|
|
303
|
+
self._assistant_content_widget = None
|
|
304
|
+
self._assistant_content_text = None
|
|
305
|
+
|
|
306
|
+
renderable = None
|
|
307
|
+
if event_type == AgentStreamEventType.TOOL_INTERACTION_LOG_ENTRY: renderable = renderables.render_tool_interaction_log(event.data)
|
|
308
|
+
elif event_type == AgentStreamEventType.TOOL_INVOCATION_AUTO_EXECUTING: renderable = renderables.render_tool_auto_executing(event.data)
|
|
309
|
+
elif event_type == AgentStreamEventType.TOOL_INVOCATION_APPROVAL_REQUESTED:
|
|
310
|
+
renderable = renderables.render_tool_approval_request(event.data)
|
|
311
|
+
self._pending_approval_data = event.data
|
|
312
|
+
await self._show_approval_prompt()
|
|
313
|
+
elif event_type == AgentStreamEventType.ERROR_EVENT: renderable = renderables.render_error(event.data)
|
|
314
|
+
elif event_type == AgentStreamEventType.SYSTEM_TASK_NOTIFICATION: renderable = renderables.render_system_task_notification(event.data) # NEW
|
|
315
|
+
|
|
316
|
+
if renderable:
|
|
317
|
+
await log_container.mount(Static(""))
|
|
318
|
+
await log_container.mount(Static(renderable))
|
|
319
|
+
|
|
320
|
+
log_container.scroll_end(animate=False)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/cli/agent_team_tui/widgets/logo.py
|
|
2
|
+
"""
|
|
3
|
+
Defines a widget to display the AutoByteus ASCII art logo and tagline.
|
|
4
|
+
"""
|
|
5
|
+
from rich.text import Text
|
|
6
|
+
from textual.widgets import Static
|
|
7
|
+
from textual.containers import Vertical
|
|
8
|
+
|
|
9
|
+
class Logo(Vertical):
|
|
10
|
+
"""A widget to display the AutoByteus logo and tagline."""
|
|
11
|
+
|
|
12
|
+
def compose(self) -> None:
|
|
13
|
+
# A simple, clean, single-line text logo that is more readable
|
|
14
|
+
# and respects that "AutoByteus" is one word.
|
|
15
|
+
logo_text = Text(justify="center")
|
|
16
|
+
logo_text.append("Auto", style="bold cyan")
|
|
17
|
+
logo_text.append("Byteus", style="bold magenta")
|
|
18
|
+
|
|
19
|
+
yield Static(logo_text, classes="logo-art")
|
|
20
|
+
yield Static("Orchestrating AI Agent Teams.", classes="logo-tagline")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/cli/agent_team_tui/widgets/renderables.py
|
|
2
|
+
"""
|
|
3
|
+
Contains pure functions that convert agent event data into Rich renderables for the FocusPane.
|
|
4
|
+
This separates presentation logic from the view logic of the widget itself.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from autobyteus.agent.streaming.stream_event_payloads import (
|
|
13
|
+
AgentOperationalPhaseTransitionData, AssistantCompleteResponseData,
|
|
14
|
+
ErrorEventData, ToolInteractionLogEntryData, ToolInvocationApprovalRequestedData,
|
|
15
|
+
ToolInvocationAutoExecutingData, SystemTaskNotificationData
|
|
16
|
+
)
|
|
17
|
+
from .shared import ASSISTANT_ICON, TOOL_ICON, PROMPT_ICON, ERROR_ICON, LOG_ICON, SYSTEM_TASK_ICON
|
|
18
|
+
|
|
19
|
+
def render_assistant_complete_response(data: AssistantCompleteResponseData) -> list[Text | Panel]:
|
|
20
|
+
"""Renders a complete, pre-aggregated assistant response."""
|
|
21
|
+
renderables = []
|
|
22
|
+
if data.reasoning:
|
|
23
|
+
reasoning_text = Text("<Thinking>\n", style="dim italic cyan")
|
|
24
|
+
reasoning_text.append(data.reasoning)
|
|
25
|
+
reasoning_text.append("\n</Thinking>", style="dim italic cyan")
|
|
26
|
+
renderables.append(reasoning_text)
|
|
27
|
+
|
|
28
|
+
if data.content:
|
|
29
|
+
content_text = Text()
|
|
30
|
+
content_text.append(f"{ASSISTANT_ICON} assistant: ", style="bold green")
|
|
31
|
+
content_text.append(data.content)
|
|
32
|
+
renderables.append(content_text)
|
|
33
|
+
|
|
34
|
+
return renderables
|
|
35
|
+
|
|
36
|
+
def render_tool_interaction_log(data: ToolInteractionLogEntryData) -> Text:
|
|
37
|
+
"""Renders a tool interaction log entry."""
|
|
38
|
+
return Text(f"{LOG_ICON} [tool-log] {data.log_entry}", style="dim")
|
|
39
|
+
|
|
40
|
+
def render_tool_auto_executing(data: ToolInvocationAutoExecutingData) -> Text:
|
|
41
|
+
"""Renders a notification that a tool is being executed automatically."""
|
|
42
|
+
try:
|
|
43
|
+
args_str = json.dumps(data.arguments, indent=2)
|
|
44
|
+
except (TypeError, OverflowError):
|
|
45
|
+
args_str = str(data.arguments)
|
|
46
|
+
|
|
47
|
+
text_content = Text(f"{TOOL_ICON} Executing tool '", style="default")
|
|
48
|
+
text_content.append(f"{data.tool_name}", style="bold yellow")
|
|
49
|
+
text_content.append("' with arguments:\n", style="default")
|
|
50
|
+
text_content.append(args_str, style="yellow")
|
|
51
|
+
return text_content
|
|
52
|
+
|
|
53
|
+
def render_tool_approval_request(data: ToolInvocationApprovalRequestedData) -> Text:
|
|
54
|
+
"""Renders a prompt for the user to approve a tool call."""
|
|
55
|
+
try:
|
|
56
|
+
args_str = json.dumps(data.arguments, indent=2)
|
|
57
|
+
except (TypeError, OverflowError):
|
|
58
|
+
args_str = str(data.arguments)
|
|
59
|
+
|
|
60
|
+
text_content = Text(f"{PROMPT_ICON} Requesting approval for tool '", style="default")
|
|
61
|
+
text_content.append(f"{data.tool_name}", style="bold yellow")
|
|
62
|
+
text_content.append("' with arguments:\n", style="default")
|
|
63
|
+
text_content.append(args_str, style="yellow")
|
|
64
|
+
return text_content
|
|
65
|
+
|
|
66
|
+
def render_error(data: ErrorEventData) -> Text:
|
|
67
|
+
"""Renders an error event."""
|
|
68
|
+
error_text = f"Error from {data.source}: {data.message}"
|
|
69
|
+
if data.details:
|
|
70
|
+
error_text += f"\nDetails: {data.details}"
|
|
71
|
+
return Text(f"{ERROR_ICON} {error_text}", style="bold red")
|
|
72
|
+
|
|
73
|
+
def render_system_task_notification(data: SystemTaskNotificationData) -> Text:
|
|
74
|
+
"""Renders a system-generated task notification."""
|
|
75
|
+
text_content = Text(f"{SYSTEM_TASK_ICON} System Task Notification: ", style="bold magenta")
|
|
76
|
+
text_content.append(data.content, style="magenta")
|
|
77
|
+
return text_content
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared constants and data for TUI widgets.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from autobyteus.agent.phases import AgentOperationalPhase
|
|
6
|
+
from autobyteus.agent_team.phases import AgentTeamOperationalPhase
|
|
7
|
+
from autobyteus.task_management.base_task_board import TaskStatus
|
|
8
|
+
|
|
9
|
+
AGENT_PHASE_ICONS: Dict[AgentOperationalPhase, str] = {
|
|
10
|
+
AgentOperationalPhase.UNINITIALIZED: "⚪",
|
|
11
|
+
AgentOperationalPhase.BOOTSTRAPPING: "⏳",
|
|
12
|
+
AgentOperationalPhase.IDLE: "🟢",
|
|
13
|
+
AgentOperationalPhase.PROCESSING_USER_INPUT: "💭",
|
|
14
|
+
AgentOperationalPhase.AWAITING_LLM_RESPONSE: "💭",
|
|
15
|
+
AgentOperationalPhase.ANALYZING_LLM_RESPONSE: "🤔",
|
|
16
|
+
AgentOperationalPhase.AWAITING_TOOL_APPROVAL: "❓",
|
|
17
|
+
AgentOperationalPhase.TOOL_DENIED: "❌",
|
|
18
|
+
AgentOperationalPhase.EXECUTING_TOOL: "🛠️",
|
|
19
|
+
AgentOperationalPhase.PROCESSING_TOOL_RESULT: "⚙️",
|
|
20
|
+
AgentOperationalPhase.SHUTTING_DOWN: "🌙",
|
|
21
|
+
AgentOperationalPhase.SHUTDOWN_COMPLETE: "⚫",
|
|
22
|
+
AgentOperationalPhase.ERROR: "❗",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
TEAM_PHASE_ICONS: Dict[AgentTeamOperationalPhase, str] = {
|
|
26
|
+
AgentTeamOperationalPhase.UNINITIALIZED: "⚪",
|
|
27
|
+
AgentTeamOperationalPhase.BOOTSTRAPPING: "⏳",
|
|
28
|
+
AgentTeamOperationalPhase.IDLE: "🟢",
|
|
29
|
+
AgentTeamOperationalPhase.PROCESSING: "⚙️",
|
|
30
|
+
AgentTeamOperationalPhase.SHUTTING_DOWN: "🌙",
|
|
31
|
+
AgentTeamOperationalPhase.SHUTDOWN_COMPLETE: "⚫",
|
|
32
|
+
AgentTeamOperationalPhase.ERROR: "❗",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TASK_STATUS_ICONS: Dict[TaskStatus, str] = {
|
|
36
|
+
TaskStatus.NOT_STARTED: "⚪",
|
|
37
|
+
TaskStatus.IN_PROGRESS: "⏳",
|
|
38
|
+
TaskStatus.COMPLETED: "✅",
|
|
39
|
+
TaskStatus.FAILED: "❌",
|
|
40
|
+
TaskStatus.BLOCKED: "🔒",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Main component icons
|
|
44
|
+
SUB_TEAM_ICON = "📂"
|
|
45
|
+
TEAM_ICON = "🏁"
|
|
46
|
+
AGENT_ICON = "🤖"
|
|
47
|
+
|
|
48
|
+
# General UI icons
|
|
49
|
+
SPEAKING_ICON = "🔊"
|
|
50
|
+
DEFAULT_ICON = "❓"
|
|
51
|
+
|
|
52
|
+
# Semantic icons for log entries
|
|
53
|
+
USER_ICON = "👤"
|
|
54
|
+
ASSISTANT_ICON = "🤖"
|
|
55
|
+
TOOL_ICON = "🛠️"
|
|
56
|
+
PROMPT_ICON = "❓"
|
|
57
|
+
ERROR_ICON = "💥"
|
|
58
|
+
PHASE_ICON = "🔄"
|
|
59
|
+
LOG_ICON = "📄"
|
|
60
|
+
SYSTEM_TASK_ICON = "📥" # NEW
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/cli/agent_team_tui/widgets/status_bar.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the status bar widget for the TUI.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from textual.widgets import Footer
|
|
7
|
+
|
|
8
|
+
class StatusBar(Footer):
|
|
9
|
+
"""A simple footer widget that displays key bindings."""
|
|
10
|
+
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
super().__init__()
|
|
13
|
+
# This will be automatically populated by Textual's binding system.
|
|
14
|
+
# You can add more status information here if needed in the future.
|