fast-agent-mcp 0.3.13__py3-none-any.whl → 0.3.14__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.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- fast_agent/agents/llm_agent.py +14 -33
- fast_agent/agents/llm_decorator.py +13 -2
- fast_agent/agents/mcp_agent.py +18 -2
- fast_agent/agents/tool_agent.py +8 -10
- fast_agent/cli/commands/check_config.py +45 -1
- fast_agent/config.py +63 -0
- fast_agent/constants.py +3 -0
- fast_agent/context.py +42 -9
- fast_agent/core/logging/listeners.py +1 -1
- fast_agent/event_progress.py +2 -3
- fast_agent/interfaces.py +9 -2
- fast_agent/llm/model_factory.py +4 -0
- fast_agent/llm/provider_key_manager.py +1 -0
- fast_agent/llm/provider_types.py +1 -0
- fast_agent/llm/request_params.py +3 -1
- fast_agent/mcp/mcp_aggregator.py +313 -40
- fast_agent/mcp/mcp_connection_manager.py +39 -9
- fast_agent/mcp/skybridge.py +45 -0
- fast_agent/mcp/sse_tracking.py +287 -0
- fast_agent/mcp/transport_tracking.py +37 -3
- fast_agent/mcp/types.py +24 -0
- fast_agent/resources/examples/workflows/router.py +1 -0
- fast_agent/resources/setup/fastagent.config.yaml +5 -0
- fast_agent/ui/console_display.py +295 -18
- fast_agent/ui/enhanced_prompt.py +107 -58
- fast_agent/ui/interactive_prompt.py +57 -34
- fast_agent/ui/mcp_display.py +108 -27
- fast_agent/ui/rich_progress.py +4 -1
- {fast_agent_mcp-0.3.13.dist-info → fast_agent_mcp-0.3.14.dist-info}/METADATA +2 -2
- {fast_agent_mcp-0.3.13.dist-info → fast_agent_mcp-0.3.14.dist-info}/RECORD +33 -30
- {fast_agent_mcp-0.3.13.dist-info → fast_agent_mcp-0.3.14.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.13.dist-info → fast_agent_mcp-0.3.14.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.13.dist-info → fast_agent_mcp-0.3.14.dist-info}/licenses/LICENSE +0 -0
fast_agent/ui/console_display.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from json import JSONDecodeError
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Set, Tuple, Union
|
|
4
4
|
|
|
5
5
|
from mcp.types import CallToolResult
|
|
6
6
|
from rich.panel import Panel
|
|
@@ -18,6 +18,7 @@ from fast_agent.ui.mermaid_utils import (
|
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
20
|
from fast_agent.mcp.prompt_message_extended import PromptMessageExtended
|
|
21
|
+
from fast_agent.mcp.skybridge import SkybridgeServerConfig
|
|
21
22
|
|
|
22
23
|
CODE_STYLE = "native"
|
|
23
24
|
|
|
@@ -538,8 +539,21 @@ class ConsoleDisplay:
|
|
|
538
539
|
|
|
539
540
|
return formatted
|
|
540
541
|
|
|
541
|
-
def show_tool_result(
|
|
542
|
-
|
|
542
|
+
def show_tool_result(
|
|
543
|
+
self,
|
|
544
|
+
result: CallToolResult,
|
|
545
|
+
name: str | None = None,
|
|
546
|
+
tool_name: str | None = None,
|
|
547
|
+
skybridge_config: "SkybridgeServerConfig | None" = None,
|
|
548
|
+
) -> None:
|
|
549
|
+
"""Display a tool result in the new visual style.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
result: The tool result to display
|
|
553
|
+
name: Optional agent name
|
|
554
|
+
tool_name: Optional tool name for skybridge detection
|
|
555
|
+
skybridge_config: Optional skybridge configuration for the server
|
|
556
|
+
"""
|
|
543
557
|
if not self.config or not self.config.logger.show_tools:
|
|
544
558
|
return
|
|
545
559
|
|
|
@@ -548,6 +562,20 @@ class ConsoleDisplay:
|
|
|
548
562
|
|
|
549
563
|
# Analyze content to determine display format and status
|
|
550
564
|
content = result.content
|
|
565
|
+
structured_content = getattr(result, "structuredContent", None)
|
|
566
|
+
has_structured = structured_content is not None
|
|
567
|
+
|
|
568
|
+
# Determine if this is a skybridge tool
|
|
569
|
+
is_skybridge_tool = False
|
|
570
|
+
skybridge_resource_uri = None
|
|
571
|
+
if has_structured and tool_name and skybridge_config:
|
|
572
|
+
# Check if this tool is a valid skybridge tool
|
|
573
|
+
for tool_cfg in skybridge_config.tools:
|
|
574
|
+
if tool_cfg.tool_name == tool_name and tool_cfg.is_valid:
|
|
575
|
+
is_skybridge_tool = True
|
|
576
|
+
skybridge_resource_uri = tool_cfg.resource_uri
|
|
577
|
+
break
|
|
578
|
+
|
|
551
579
|
if result.isError:
|
|
552
580
|
status = "ERROR"
|
|
553
581
|
else:
|
|
@@ -575,9 +603,9 @@ class ConsoleDisplay:
|
|
|
575
603
|
if channel == "post-json":
|
|
576
604
|
transport_info = "HTTP (JSON-RPC)"
|
|
577
605
|
elif channel == "post-sse":
|
|
578
|
-
transport_info = "
|
|
606
|
+
transport_info = "Legacy SSE"
|
|
579
607
|
elif channel == "get":
|
|
580
|
-
transport_info = "
|
|
608
|
+
transport_info = "Legacy SSE"
|
|
581
609
|
elif channel == "resumption":
|
|
582
610
|
transport_info = "Resumption"
|
|
583
611
|
elif channel == "stdio":
|
|
@@ -591,21 +619,138 @@ class ConsoleDisplay:
|
|
|
591
619
|
if isinstance(elapsed, (int, float)):
|
|
592
620
|
bottom_metadata_items.append(self._format_elapsed(float(elapsed)))
|
|
593
621
|
|
|
622
|
+
# Add structured content indicator if present
|
|
623
|
+
if has_structured:
|
|
624
|
+
bottom_metadata_items.append("Structured ■")
|
|
625
|
+
|
|
594
626
|
bottom_metadata = bottom_metadata_items or None
|
|
595
627
|
|
|
596
628
|
# Build right info (without channel info)
|
|
597
629
|
right_info = f"[dim]tool result - {status}[/dim]"
|
|
598
630
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
content
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
631
|
+
if has_structured:
|
|
632
|
+
# Handle structured content display manually to insert it before bottom separator
|
|
633
|
+
# Display main content without bottom separator
|
|
634
|
+
config = MESSAGE_CONFIGS[MessageType.TOOL_RESULT]
|
|
635
|
+
block_color = "red" if result.isError else config["block_color"]
|
|
636
|
+
arrow = config["arrow"]
|
|
637
|
+
arrow_style = config["arrow_style"]
|
|
638
|
+
left = f"[{block_color}]▎[/{block_color}][{arrow_style}]{arrow}[/{arrow_style}]"
|
|
639
|
+
if name:
|
|
640
|
+
left += f" [{block_color if not result.isError else 'red'}]{name}[/{block_color if not result.isError else 'red'}]"
|
|
641
|
+
|
|
642
|
+
# Top separator
|
|
643
|
+
self._create_combined_separator_status(left, right_info)
|
|
644
|
+
|
|
645
|
+
# Main content
|
|
646
|
+
self._display_content(
|
|
647
|
+
content, True, result.isError, MessageType.TOOL_RESULT, check_markdown_markers=False
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
# Structured content separator and display
|
|
651
|
+
console.console.print()
|
|
652
|
+
total_width = console.console.size.width
|
|
653
|
+
|
|
654
|
+
if is_skybridge_tool:
|
|
655
|
+
# Skybridge: magenta separator with resource URI
|
|
656
|
+
resource_label = (
|
|
657
|
+
f"skybridge resource: {skybridge_resource_uri}"
|
|
658
|
+
if skybridge_resource_uri
|
|
659
|
+
else "skybridge resource"
|
|
660
|
+
)
|
|
661
|
+
prefix = Text("─| ")
|
|
662
|
+
prefix.stylize("dim")
|
|
663
|
+
resource_text = Text(resource_label, style="magenta")
|
|
664
|
+
suffix = Text(" |")
|
|
665
|
+
suffix.stylize("dim")
|
|
666
|
+
|
|
667
|
+
separator_line = Text()
|
|
668
|
+
separator_line.append_text(prefix)
|
|
669
|
+
separator_line.append_text(resource_text)
|
|
670
|
+
separator_line.append_text(suffix)
|
|
671
|
+
remaining = total_width - separator_line.cell_len
|
|
672
|
+
if remaining > 0:
|
|
673
|
+
separator_line.append("─" * remaining, style="dim")
|
|
674
|
+
console.console.print(separator_line, markup=self._markup)
|
|
675
|
+
console.console.print()
|
|
676
|
+
|
|
677
|
+
# Display with bright syntax highlighting
|
|
678
|
+
import json
|
|
679
|
+
|
|
680
|
+
from rich.syntax import Syntax
|
|
681
|
+
|
|
682
|
+
json_str = json.dumps(structured_content, indent=2)
|
|
683
|
+
syntax_obj = Syntax(json_str, "json", theme=CODE_STYLE, background_color="default")
|
|
684
|
+
console.console.print(syntax_obj, markup=self._markup)
|
|
685
|
+
else:
|
|
686
|
+
# Regular tool: dim separator
|
|
687
|
+
prefix = Text("─| ")
|
|
688
|
+
prefix.stylize("dim")
|
|
689
|
+
label_text = Text("Structured Content", style="dim")
|
|
690
|
+
suffix = Text(" |")
|
|
691
|
+
suffix.stylize("dim")
|
|
692
|
+
|
|
693
|
+
separator_line = Text()
|
|
694
|
+
separator_line.append_text(prefix)
|
|
695
|
+
separator_line.append_text(label_text)
|
|
696
|
+
separator_line.append_text(suffix)
|
|
697
|
+
remaining = total_width - separator_line.cell_len
|
|
698
|
+
if remaining > 0:
|
|
699
|
+
separator_line.append("─" * remaining, style="dim")
|
|
700
|
+
console.console.print(separator_line, markup=self._markup)
|
|
701
|
+
console.console.print()
|
|
702
|
+
|
|
703
|
+
# Display truncated content in dim
|
|
704
|
+
from rich.pretty import Pretty
|
|
705
|
+
|
|
706
|
+
if self.config and self.config.logger.truncate_tools:
|
|
707
|
+
pretty_obj = Pretty(structured_content, max_length=10, max_string=50)
|
|
708
|
+
else:
|
|
709
|
+
pretty_obj = Pretty(structured_content)
|
|
710
|
+
console.console.print(pretty_obj, style="dim", markup=self._markup)
|
|
711
|
+
|
|
712
|
+
# Bottom separator with metadata
|
|
713
|
+
console.console.print()
|
|
714
|
+
if bottom_metadata:
|
|
715
|
+
display_items = (
|
|
716
|
+
self._shorten_items(bottom_metadata, 12) if True else bottom_metadata
|
|
717
|
+
)
|
|
718
|
+
prefix = Text("─| ")
|
|
719
|
+
prefix.stylize("dim")
|
|
720
|
+
suffix = Text(" |")
|
|
721
|
+
suffix.stylize("dim")
|
|
722
|
+
available = max(0, total_width - prefix.cell_len - suffix.cell_len)
|
|
723
|
+
|
|
724
|
+
metadata_text = self._format_bottom_metadata(
|
|
725
|
+
display_items,
|
|
726
|
+
None,
|
|
727
|
+
config["highlight_color"],
|
|
728
|
+
max_width=available,
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
line = Text()
|
|
732
|
+
line.append_text(prefix)
|
|
733
|
+
line.append_text(metadata_text)
|
|
734
|
+
line.append_text(suffix)
|
|
735
|
+
remaining = total_width - line.cell_len
|
|
736
|
+
if remaining > 0:
|
|
737
|
+
line.append("─" * remaining, style="dim")
|
|
738
|
+
console.console.print(line, markup=self._markup)
|
|
739
|
+
else:
|
|
740
|
+
console.console.print("─" * total_width, style="dim")
|
|
741
|
+
console.console.print()
|
|
742
|
+
|
|
743
|
+
else:
|
|
744
|
+
# No structured content - use standard display
|
|
745
|
+
self.display_message(
|
|
746
|
+
content=content,
|
|
747
|
+
message_type=MessageType.TOOL_RESULT,
|
|
748
|
+
name=name,
|
|
749
|
+
right_info=right_info,
|
|
750
|
+
bottom_metadata=bottom_metadata,
|
|
751
|
+
is_error=result.isError,
|
|
752
|
+
truncate_content=True,
|
|
753
|
+
)
|
|
609
754
|
|
|
610
755
|
def show_tool_call(
|
|
611
756
|
self,
|
|
@@ -669,9 +814,7 @@ class ConsoleDisplay:
|
|
|
669
814
|
# No active prompt_toolkit session - display with rich as before
|
|
670
815
|
# Combined separator and status line
|
|
671
816
|
if agent_name:
|
|
672
|
-
left =
|
|
673
|
-
f"[magenta]▎[/magenta][dim magenta]▶[/dim magenta] [magenta]{agent_name}[/magenta]"
|
|
674
|
-
)
|
|
817
|
+
left = f"[magenta]▎[/magenta][dim magenta]▶[/dim magenta] [magenta]{agent_name}[/magenta]"
|
|
675
818
|
else:
|
|
676
819
|
left = "[magenta]▎[/magenta][dim magenta]▶[/dim magenta]"
|
|
677
820
|
|
|
@@ -727,6 +870,140 @@ class ConsoleDisplay:
|
|
|
727
870
|
console.console.print(combined, markup=self._markup)
|
|
728
871
|
console.console.print()
|
|
729
872
|
|
|
873
|
+
@staticmethod
|
|
874
|
+
def summarize_skybridge_configs(
|
|
875
|
+
configs: Mapping[str, "SkybridgeServerConfig"] | None,
|
|
876
|
+
) -> Tuple[List[Dict[str, Any]], List[str]]:
|
|
877
|
+
"""Convert raw Skybridge configs into display-friendly summary data."""
|
|
878
|
+
server_rows: List[Dict[str, Any]] = []
|
|
879
|
+
warnings: List[str] = []
|
|
880
|
+
warning_seen: Set[str] = set()
|
|
881
|
+
|
|
882
|
+
if not configs:
|
|
883
|
+
return server_rows, warnings
|
|
884
|
+
|
|
885
|
+
def add_warning(message: str) -> None:
|
|
886
|
+
formatted = message.strip()
|
|
887
|
+
if not formatted:
|
|
888
|
+
return
|
|
889
|
+
if formatted not in warning_seen:
|
|
890
|
+
warnings.append(formatted)
|
|
891
|
+
warning_seen.add(formatted)
|
|
892
|
+
|
|
893
|
+
for server_name in sorted(configs.keys()):
|
|
894
|
+
config = configs.get(server_name)
|
|
895
|
+
if not config:
|
|
896
|
+
continue
|
|
897
|
+
resources = list(config.ui_resources or [])
|
|
898
|
+
has_skybridge_signal = bool(
|
|
899
|
+
config.enabled or resources or config.tools or config.warnings
|
|
900
|
+
)
|
|
901
|
+
if not has_skybridge_signal:
|
|
902
|
+
continue
|
|
903
|
+
|
|
904
|
+
valid_resource_count = sum(1 for resource in resources if resource.is_skybridge)
|
|
905
|
+
|
|
906
|
+
server_rows.append(
|
|
907
|
+
{
|
|
908
|
+
"server_name": server_name,
|
|
909
|
+
"config": config,
|
|
910
|
+
"resources": resources,
|
|
911
|
+
"valid_resource_count": valid_resource_count,
|
|
912
|
+
"total_resource_count": len(resources),
|
|
913
|
+
"active_tools": [
|
|
914
|
+
{
|
|
915
|
+
"name": tool.display_name,
|
|
916
|
+
"template": str(tool.template_uri) if tool.template_uri else None,
|
|
917
|
+
}
|
|
918
|
+
for tool in config.tools
|
|
919
|
+
if tool.is_valid
|
|
920
|
+
],
|
|
921
|
+
"enabled": config.enabled,
|
|
922
|
+
}
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
for warning in config.warnings:
|
|
926
|
+
message = warning.strip()
|
|
927
|
+
if not message:
|
|
928
|
+
continue
|
|
929
|
+
if not message.startswith(server_name):
|
|
930
|
+
message = f"{server_name} {message}"
|
|
931
|
+
add_warning(message)
|
|
932
|
+
|
|
933
|
+
return server_rows, warnings
|
|
934
|
+
|
|
935
|
+
def show_skybridge_summary(
|
|
936
|
+
self,
|
|
937
|
+
agent_name: str,
|
|
938
|
+
configs: Mapping[str, "SkybridgeServerConfig"] | None,
|
|
939
|
+
) -> None:
|
|
940
|
+
"""Display Skybridge availability and warnings."""
|
|
941
|
+
server_rows, warnings = self.summarize_skybridge_configs(configs)
|
|
942
|
+
|
|
943
|
+
if not server_rows and not warnings:
|
|
944
|
+
return
|
|
945
|
+
|
|
946
|
+
heading = "[dim]OpenAI Apps SDK ([/dim][cyan]skybridge[/cyan][dim]) detected:[/dim]"
|
|
947
|
+
console.console.print()
|
|
948
|
+
console.console.print(heading, markup=self._markup)
|
|
949
|
+
|
|
950
|
+
if not server_rows:
|
|
951
|
+
console.console.print("[dim] ● none detected[/dim]", markup=self._markup)
|
|
952
|
+
else:
|
|
953
|
+
for row in server_rows:
|
|
954
|
+
server_name = row["server_name"]
|
|
955
|
+
resource_count = row["valid_resource_count"]
|
|
956
|
+
total_resource_count = row["total_resource_count"]
|
|
957
|
+
tool_infos = row["active_tools"]
|
|
958
|
+
enabled = row["enabled"]
|
|
959
|
+
|
|
960
|
+
tool_count = len(tool_infos)
|
|
961
|
+
tool_word = "tool" if tool_count == 1 else "tools"
|
|
962
|
+
resource_word = (
|
|
963
|
+
"skybridge resource" if resource_count == 1 else "skybridge resources"
|
|
964
|
+
)
|
|
965
|
+
tool_segment = f"[cyan]{tool_count}[/cyan][dim] {tool_word}[/dim]"
|
|
966
|
+
resource_segment = f"[cyan]{resource_count}[/cyan][dim] {resource_word}[/dim]"
|
|
967
|
+
name_style = "cyan" if enabled else "yellow"
|
|
968
|
+
status_suffix = "" if enabled else "[dim] (issues detected)[/dim]"
|
|
969
|
+
|
|
970
|
+
console.console.print(
|
|
971
|
+
f"[dim] ● [/dim][{name_style}]{server_name}[/{name_style}]{status_suffix}"
|
|
972
|
+
f"[dim] — [/dim]{tool_segment}[dim], [/dim]{resource_segment}",
|
|
973
|
+
markup=self._markup,
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
for tool_info in tool_infos:
|
|
977
|
+
template_text = (
|
|
978
|
+
f"[dim] ({tool_info['template']})[/dim]" if tool_info["template"] else ""
|
|
979
|
+
)
|
|
980
|
+
console.console.print(
|
|
981
|
+
f"[dim] ▶ [/dim][white]{tool_info['name']}[/white]{template_text}",
|
|
982
|
+
markup=self._markup,
|
|
983
|
+
)
|
|
984
|
+
|
|
985
|
+
if tool_count == 0 and resource_count > 0:
|
|
986
|
+
console.console.print(
|
|
987
|
+
"[dim] ▶ tools not linked[/dim]",
|
|
988
|
+
markup=self._markup,
|
|
989
|
+
)
|
|
990
|
+
if not enabled and total_resource_count > resource_count:
|
|
991
|
+
invalid_count = total_resource_count - resource_count
|
|
992
|
+
invalid_word = "resource" if invalid_count == 1 else "resources"
|
|
993
|
+
console.console.print(
|
|
994
|
+
(
|
|
995
|
+
"[dim] ▶ "
|
|
996
|
+
f"[/dim][cyan]{invalid_count}[/cyan][dim] {invalid_word} detected with non-skybridge MIME type[/dim]"
|
|
997
|
+
),
|
|
998
|
+
markup=self._markup,
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
for warning_entry in warnings:
|
|
1002
|
+
console.console.print(
|
|
1003
|
+
f"[dim red] ▶ [/dim red][red]warning[/red] [dim]{warning_entry}[/dim]",
|
|
1004
|
+
markup=self._markup,
|
|
1005
|
+
)
|
|
1006
|
+
|
|
730
1007
|
async def show_assistant_message(
|
|
731
1008
|
self,
|
|
732
1009
|
message_text: Union[str, Text, "PromptMessageExtended"],
|
fast_agent/ui/enhanced_prompt.py
CHANGED
|
@@ -9,7 +9,7 @@ import shlex
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import tempfile
|
|
11
11
|
from importlib.metadata import version
|
|
12
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
13
13
|
|
|
14
14
|
from prompt_toolkit import PromptSession
|
|
15
15
|
from prompt_toolkit.completion import Completer, Completion, WordCompleter
|
|
@@ -24,6 +24,7 @@ from fast_agent.agents.agent_types import AgentType
|
|
|
24
24
|
from fast_agent.constants import FAST_AGENT_ERROR_CHANNEL, FAST_AGENT_REMOVED_METADATA_CHANNEL
|
|
25
25
|
from fast_agent.core.exceptions import PromptExitError
|
|
26
26
|
from fast_agent.llm.model_info import get_model_info
|
|
27
|
+
from fast_agent.mcp.types import McpAgentProtocol
|
|
27
28
|
from fast_agent.ui.mcp_display import render_mcp_status
|
|
28
29
|
|
|
29
30
|
if TYPE_CHECKING:
|
|
@@ -103,10 +104,9 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: "AgentApp
|
|
|
103
104
|
|
|
104
105
|
# Get counts TODO -- add this to the type library or adjust the way aggregator/reporting works
|
|
105
106
|
server_count = 0
|
|
106
|
-
if
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
)
|
|
107
|
+
if isinstance(agent, McpAgentProtocol):
|
|
108
|
+
server_names = agent.aggregator.server_names
|
|
109
|
+
server_count = len(server_names) if server_names else 0
|
|
110
110
|
|
|
111
111
|
tools_result = await agent.list_tools()
|
|
112
112
|
tool_count = (
|
|
@@ -182,6 +182,17 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: "AgentApp
|
|
|
182
182
|
rich_print(f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {content}")
|
|
183
183
|
# await _render_mcp_status(agent)
|
|
184
184
|
|
|
185
|
+
# Display Skybridge status (if aggregator discovered any)
|
|
186
|
+
try:
|
|
187
|
+
aggregator = agent.aggregator if isinstance(agent, McpAgentProtocol) else None
|
|
188
|
+
display = getattr(agent, "display", None)
|
|
189
|
+
if aggregator and display and hasattr(display, "show_skybridge_summary"):
|
|
190
|
+
skybridge_configs = await aggregator.get_skybridge_configs()
|
|
191
|
+
display.show_skybridge_summary(agent_name, skybridge_configs)
|
|
192
|
+
except Exception:
|
|
193
|
+
# Ignore Skybridge rendering issues to avoid interfering with startup
|
|
194
|
+
pass
|
|
195
|
+
|
|
185
196
|
# Mark as shown
|
|
186
197
|
_agent_info_shown.add(agent_name)
|
|
187
198
|
|
|
@@ -648,60 +659,96 @@ async def get_enhanced_input(
|
|
|
648
659
|
model_display = None
|
|
649
660
|
tdv_segment = None
|
|
650
661
|
turn_count = 0
|
|
651
|
-
|
|
652
|
-
|
|
662
|
+
agent = None
|
|
663
|
+
if agent_provider:
|
|
664
|
+
try:
|
|
653
665
|
agent = agent_provider._agent(agent_name)
|
|
666
|
+
except Exception as exc:
|
|
667
|
+
print(f"[toolbar debug] unable to resolve agent '{agent_name}': {exc}")
|
|
654
668
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
669
|
+
if agent:
|
|
670
|
+
for message in agent.message_history:
|
|
671
|
+
if message.role == "user":
|
|
672
|
+
turn_count += 1
|
|
673
|
+
|
|
674
|
+
# Resolve LLM reference safely (avoid assertion when unattached)
|
|
675
|
+
llm = None
|
|
676
|
+
try:
|
|
677
|
+
llm = agent.llm
|
|
678
|
+
except AssertionError:
|
|
679
|
+
llm = getattr(agent, "_llm", None)
|
|
680
|
+
except Exception as exc:
|
|
681
|
+
print(f"[toolbar debug] agent.llm access failed for '{agent_name}': {exc}")
|
|
682
|
+
|
|
683
|
+
model_name = None
|
|
684
|
+
if llm:
|
|
685
|
+
model_name = getattr(llm, "model_name", None)
|
|
686
|
+
if not model_name:
|
|
687
|
+
model_name = getattr(getattr(llm, "default_request_params", None), "model", None)
|
|
688
|
+
|
|
689
|
+
if not model_name:
|
|
690
|
+
model_name = getattr(agent.config, "model", None)
|
|
691
|
+
if not model_name and getattr(agent.config, "default_request_params", None):
|
|
692
|
+
model_name = getattr(agent.config.default_request_params, "model", None)
|
|
693
|
+
if not model_name:
|
|
694
|
+
context = getattr(agent, "context", None) or getattr(agent_provider, "context", None)
|
|
695
|
+
config_obj = getattr(context, "config", None) if context else None
|
|
696
|
+
model_name = getattr(config_obj, "default_model", None)
|
|
697
|
+
|
|
698
|
+
if model_name:
|
|
699
|
+
max_len = 25
|
|
700
|
+
model_display = model_name[: max_len - 1] + "…" if len(model_name) > max_len else model_name
|
|
701
|
+
else:
|
|
702
|
+
print(f"[toolbar debug] no model resolved for agent '{agent_name}'")
|
|
703
|
+
model_display = "unknown"
|
|
704
|
+
|
|
705
|
+
# Build TDV capability segment based on model database
|
|
706
|
+
info = None
|
|
707
|
+
if llm:
|
|
708
|
+
try:
|
|
709
|
+
info = get_model_info(llm)
|
|
710
|
+
except TypeError:
|
|
711
|
+
info = None
|
|
712
|
+
if not info and model_name:
|
|
713
|
+
try:
|
|
714
|
+
info = get_model_info(model_name)
|
|
715
|
+
except TypeError:
|
|
716
|
+
info = None
|
|
717
|
+
except Exception as exc:
|
|
718
|
+
print(f"[toolbar debug] get_model_info failed for '{agent_name}': {exc}")
|
|
719
|
+
info = None
|
|
720
|
+
|
|
721
|
+
# Default to text-only if info resolution fails for any reason
|
|
722
|
+
t, d, v = (True, False, False)
|
|
723
|
+
if info:
|
|
724
|
+
t, d, v = info.tdv_flags
|
|
725
|
+
|
|
726
|
+
# Check for alert flags in user messages
|
|
727
|
+
alert_flags: set[str] = set()
|
|
728
|
+
error_seen = False
|
|
729
|
+
for message in agent.message_history:
|
|
730
|
+
if message.channels:
|
|
731
|
+
if message.channels.get(FAST_AGENT_ERROR_CHANNEL):
|
|
732
|
+
error_seen = True
|
|
733
|
+
if message.role == "user" and message.channels:
|
|
734
|
+
meta_blocks = message.channels.get(FAST_AGENT_REMOVED_METADATA_CHANNEL, [])
|
|
735
|
+
alert_flags.update(_extract_alert_flags_from_meta(meta_blocks))
|
|
736
|
+
|
|
737
|
+
if error_seen and not alert_flags:
|
|
738
|
+
alert_flags.add("T")
|
|
739
|
+
|
|
740
|
+
def _style_flag(letter: str, supported: bool) -> str:
|
|
741
|
+
# Enabled uses the same color as NORMAL mode (ansigreen), disabled is dim
|
|
742
|
+
if letter in alert_flags:
|
|
743
|
+
return f"<style fg='ansired' bg='ansiblack'>{letter}</style>"
|
|
744
|
+
|
|
745
|
+
enabled_color = "ansigreen"
|
|
746
|
+
if supported:
|
|
747
|
+
return f"<style fg='{enabled_color}' bg='ansiblack'>{letter}</style>"
|
|
748
|
+
return f"<style fg='ansiblack' bg='ansiwhite'>{letter}</style>"
|
|
749
|
+
|
|
750
|
+
tdv_segment = f"{_style_flag('T', t)}{_style_flag('D', d)}{_style_flag('V', v)}"
|
|
751
|
+
else:
|
|
705
752
|
model_display = None
|
|
706
753
|
tdv_segment = None
|
|
707
754
|
|
|
@@ -1022,7 +1069,9 @@ async def get_argument_input(
|
|
|
1022
1069
|
prompt_session.app.exit()
|
|
1023
1070
|
|
|
1024
1071
|
|
|
1025
|
-
async def handle_special_commands(
|
|
1072
|
+
async def handle_special_commands(
|
|
1073
|
+
command: Any, agent_app: "AgentApp | None" = None
|
|
1074
|
+
) -> bool | Dict[str, Any]:
|
|
1026
1075
|
"""
|
|
1027
1076
|
Handle special input commands.
|
|
1028
1077
|
|