fast-agent-mcp 0.3.5__py3-none-any.whl → 0.3.7__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/__init__.py +9 -1
- fast_agent/agents/agent_types.py +11 -11
- fast_agent/agents/llm_agent.py +76 -40
- fast_agent/agents/llm_decorator.py +355 -6
- fast_agent/agents/mcp_agent.py +154 -59
- fast_agent/agents/tool_agent.py +60 -4
- fast_agent/agents/workflow/router_agent.py +10 -2
- fast_agent/cli/commands/auth.py +52 -29
- fast_agent/cli/commands/check_config.py +26 -5
- fast_agent/cli/commands/go.py +11 -5
- fast_agent/cli/commands/setup.py +4 -7
- fast_agent/config.py +4 -1
- fast_agent/constants.py +2 -0
- fast_agent/core/agent_app.py +2 -0
- fast_agent/core/direct_factory.py +39 -120
- fast_agent/core/fastagent.py +2 -2
- fast_agent/history/history_exporter.py +3 -3
- fast_agent/llm/fastagent_llm.py +3 -3
- fast_agent/llm/provider/openai/llm_openai.py +57 -8
- fast_agent/mcp/__init__.py +1 -2
- fast_agent/mcp/mcp_aggregator.py +34 -1
- fast_agent/mcp/mcp_connection_manager.py +23 -4
- fast_agent/mcp/oauth_client.py +32 -4
- fast_agent/mcp/prompt_message_extended.py +2 -0
- fast_agent/mcp/prompt_serialization.py +124 -39
- fast_agent/mcp/prompts/prompt_load.py +34 -32
- fast_agent/mcp/prompts/prompt_server.py +26 -11
- fast_agent/resources/setup/.gitignore +6 -0
- fast_agent/resources/setup/agent.py +8 -1
- fast_agent/resources/setup/fastagent.config.yaml +2 -2
- fast_agent/resources/setup/pyproject.toml.tmpl +6 -0
- fast_agent/types/__init__.py +3 -1
- fast_agent/ui/console_display.py +48 -31
- fast_agent/ui/enhanced_prompt.py +119 -64
- fast_agent/ui/interactive_prompt.py +66 -40
- fast_agent/ui/rich_progress.py +12 -8
- {fast_agent_mcp-0.3.5.dist-info → fast_agent_mcp-0.3.7.dist-info}/METADATA +3 -3
- {fast_agent_mcp-0.3.5.dist-info → fast_agent_mcp-0.3.7.dist-info}/RECORD +41 -41
- {fast_agent_mcp-0.3.5.dist-info → fast_agent_mcp-0.3.7.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.5.dist-info → fast_agent_mcp-0.3.7.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.5.dist-info → fast_agent_mcp-0.3.7.dist-info}/licenses/LICENSE +0 -0
fast_agent/ui/console_display.py
CHANGED
|
@@ -26,6 +26,7 @@ class MessageType(Enum):
|
|
|
26
26
|
|
|
27
27
|
USER = "user"
|
|
28
28
|
ASSISTANT = "assistant"
|
|
29
|
+
SYSTEM = "system"
|
|
29
30
|
TOOL_CALL = "tool_call"
|
|
30
31
|
TOOL_RESULT = "tool_result"
|
|
31
32
|
|
|
@@ -44,6 +45,12 @@ MESSAGE_CONFIGS = {
|
|
|
44
45
|
"arrow_style": "dim green",
|
|
45
46
|
"highlight_color": "bright_green",
|
|
46
47
|
},
|
|
48
|
+
MessageType.SYSTEM: {
|
|
49
|
+
"block_color": "yellow",
|
|
50
|
+
"arrow": "●",
|
|
51
|
+
"arrow_style": "dim yellow",
|
|
52
|
+
"highlight_color": "bright_yellow",
|
|
53
|
+
},
|
|
47
54
|
MessageType.TOOL_CALL: {
|
|
48
55
|
"block_color": "magenta",
|
|
49
56
|
"arrow": "◀",
|
|
@@ -144,7 +151,7 @@ class ConsoleDisplay:
|
|
|
144
151
|
name: str | None = None,
|
|
145
152
|
right_info: str = "",
|
|
146
153
|
bottom_metadata: List[str] | None = None,
|
|
147
|
-
|
|
154
|
+
highlight_index: int | None = None,
|
|
148
155
|
max_item_length: int | None = None,
|
|
149
156
|
is_error: bool = False,
|
|
150
157
|
truncate_content: bool = True,
|
|
@@ -159,7 +166,7 @@ class ConsoleDisplay:
|
|
|
159
166
|
name: Optional name to display (agent name, user name, etc.)
|
|
160
167
|
right_info: Information to display on the right side of the header
|
|
161
168
|
bottom_metadata: Optional list of items for bottom separator
|
|
162
|
-
|
|
169
|
+
highlight_index: Index of item to highlight in bottom metadata (0-based), or None
|
|
163
170
|
max_item_length: Optional max length for bottom metadata items (with ellipsis)
|
|
164
171
|
is_error: For tool results, whether this is an error (uses red color)
|
|
165
172
|
truncate_content: Whether to truncate long content
|
|
@@ -199,16 +206,6 @@ class ConsoleDisplay:
|
|
|
199
206
|
if max_item_length:
|
|
200
207
|
display_items = self._shorten_items(bottom_metadata, max_item_length)
|
|
201
208
|
|
|
202
|
-
# Normalize highlight_items
|
|
203
|
-
if highlight_items is None:
|
|
204
|
-
highlight_items = []
|
|
205
|
-
elif isinstance(highlight_items, str):
|
|
206
|
-
highlight_items = [highlight_items]
|
|
207
|
-
|
|
208
|
-
# Shorten highlight items to match if we shortened display items
|
|
209
|
-
if max_item_length:
|
|
210
|
-
highlight_items = self._shorten_items(highlight_items, max_item_length)
|
|
211
|
-
|
|
212
209
|
# Format the metadata with highlighting, clipped to available width
|
|
213
210
|
# Compute available width for the metadata segment (excluding the fixed prefix/suffix)
|
|
214
211
|
total_width = console.console.size.width
|
|
@@ -220,7 +217,7 @@ class ConsoleDisplay:
|
|
|
220
217
|
|
|
221
218
|
metadata_text = self._format_bottom_metadata(
|
|
222
219
|
display_items,
|
|
223
|
-
|
|
220
|
+
highlight_index,
|
|
224
221
|
config["highlight_color"],
|
|
225
222
|
max_width=available,
|
|
226
223
|
)
|
|
@@ -268,11 +265,11 @@ class ConsoleDisplay:
|
|
|
268
265
|
from fast_agent.mcp.helpers.content_helpers import get_text, is_text_content
|
|
269
266
|
|
|
270
267
|
# Determine the style based on message type
|
|
271
|
-
# USER and
|
|
268
|
+
# USER, ASSISTANT, and SYSTEM messages should display in normal style
|
|
272
269
|
# TOOL_CALL and TOOL_RESULT should be dimmed
|
|
273
270
|
if is_error:
|
|
274
271
|
style = "dim red"
|
|
275
|
-
elif message_type in [MessageType.USER, MessageType.ASSISTANT]:
|
|
272
|
+
elif message_type in [MessageType.USER, MessageType.ASSISTANT, MessageType.SYSTEM]:
|
|
276
273
|
style = None # No style means default/normal white
|
|
277
274
|
else:
|
|
278
275
|
style = "dim"
|
|
@@ -464,7 +461,7 @@ class ConsoleDisplay:
|
|
|
464
461
|
def _format_bottom_metadata(
|
|
465
462
|
self,
|
|
466
463
|
items: List[str],
|
|
467
|
-
|
|
464
|
+
highlight_index: int | None,
|
|
468
465
|
highlight_color: str,
|
|
469
466
|
max_width: int | None = None,
|
|
470
467
|
) -> Text:
|
|
@@ -473,8 +470,9 @@ class ConsoleDisplay:
|
|
|
473
470
|
|
|
474
471
|
Args:
|
|
475
472
|
items: List of items to display
|
|
476
|
-
|
|
473
|
+
highlight_index: Index of item to highlight (0-based), or None for no highlighting
|
|
477
474
|
highlight_color: Color to use for highlighting
|
|
475
|
+
max_width: Maximum width for the formatted text
|
|
478
476
|
|
|
479
477
|
Returns:
|
|
480
478
|
Formatted Text object with proper separators and highlighting
|
|
@@ -491,14 +489,7 @@ class ConsoleDisplay:
|
|
|
491
489
|
sep = Text(" | ", style="dim") if i > 0 else Text("")
|
|
492
490
|
|
|
493
491
|
# Prepare item text with potential highlighting
|
|
494
|
-
should_highlight =
|
|
495
|
-
if item in highlight_items:
|
|
496
|
-
should_highlight = True
|
|
497
|
-
else:
|
|
498
|
-
for highlight in highlight_items:
|
|
499
|
-
if item.startswith(highlight) or highlight.endswith(item):
|
|
500
|
-
should_highlight = True
|
|
501
|
-
break
|
|
492
|
+
should_highlight = highlight_index is not None and i == highlight_index
|
|
502
493
|
|
|
503
494
|
item_text = Text(item, style=(highlight_color if should_highlight else "dim"))
|
|
504
495
|
|
|
@@ -569,7 +560,7 @@ class ConsoleDisplay:
|
|
|
569
560
|
tool_name: str,
|
|
570
561
|
tool_args: Dict[str, Any] | None,
|
|
571
562
|
bottom_items: List[str] | None = None,
|
|
572
|
-
|
|
563
|
+
highlight_index: int | None = None,
|
|
573
564
|
max_item_length: int | None = None,
|
|
574
565
|
name: str | None = None,
|
|
575
566
|
) -> None:
|
|
@@ -579,7 +570,7 @@ class ConsoleDisplay:
|
|
|
579
570
|
tool_name: Name of the tool being called
|
|
580
571
|
tool_args: Arguments being passed to the tool
|
|
581
572
|
bottom_items: Optional list of items for bottom separator (e.g., available tools)
|
|
582
|
-
|
|
573
|
+
highlight_index: Index of item to highlight in the bottom separator (0-based), or None
|
|
583
574
|
max_item_length: Optional max length for bottom items (with ellipsis)
|
|
584
575
|
name: Optional agent name
|
|
585
576
|
"""
|
|
@@ -596,7 +587,7 @@ class ConsoleDisplay:
|
|
|
596
587
|
name=name,
|
|
597
588
|
right_info=right_info,
|
|
598
589
|
bottom_metadata=bottom_items,
|
|
599
|
-
|
|
590
|
+
highlight_index=highlight_index,
|
|
600
591
|
max_item_length=max_item_length,
|
|
601
592
|
truncate_content=True,
|
|
602
593
|
)
|
|
@@ -683,7 +674,7 @@ class ConsoleDisplay:
|
|
|
683
674
|
self,
|
|
684
675
|
message_text: Union[str, Text, "PromptMessageExtended"],
|
|
685
676
|
bottom_items: List[str] | None = None,
|
|
686
|
-
|
|
677
|
+
highlight_index: int | None = None,
|
|
687
678
|
max_item_length: int | None = None,
|
|
688
679
|
name: str | None = None,
|
|
689
680
|
model: str | None = None,
|
|
@@ -694,7 +685,7 @@ class ConsoleDisplay:
|
|
|
694
685
|
Args:
|
|
695
686
|
message_text: The message content to display (str, Text, or PromptMessageExtended)
|
|
696
687
|
bottom_items: Optional list of items for bottom separator (e.g., servers, destinations)
|
|
697
|
-
|
|
688
|
+
highlight_index: Index of item to highlight in the bottom separator (0-based), or None
|
|
698
689
|
max_item_length: Optional max length for bottom items (with ellipsis)
|
|
699
690
|
title: Title for the message (default "ASSISTANT")
|
|
700
691
|
name: Optional agent name
|
|
@@ -722,7 +713,7 @@ class ConsoleDisplay:
|
|
|
722
713
|
name=name,
|
|
723
714
|
right_info=right_info,
|
|
724
715
|
bottom_metadata=bottom_items,
|
|
725
|
-
|
|
716
|
+
highlight_index=highlight_index,
|
|
726
717
|
max_item_length=max_item_length,
|
|
727
718
|
truncate_content=False, # Assistant messages shouldn't be truncated
|
|
728
719
|
additional_message=additional_message,
|
|
@@ -816,6 +807,32 @@ class ConsoleDisplay:
|
|
|
816
807
|
truncate_content=False, # User messages typically shouldn't be truncated
|
|
817
808
|
)
|
|
818
809
|
|
|
810
|
+
def show_system_message(
|
|
811
|
+
self,
|
|
812
|
+
system_prompt: str,
|
|
813
|
+
agent_name: str | None = None,
|
|
814
|
+
server_count: int = 0,
|
|
815
|
+
) -> None:
|
|
816
|
+
"""Display the system prompt in a formatted panel."""
|
|
817
|
+
if not self.config or not self.config.logger.show_chat:
|
|
818
|
+
return
|
|
819
|
+
|
|
820
|
+
# Build right side info
|
|
821
|
+
right_parts = []
|
|
822
|
+
if server_count > 0:
|
|
823
|
+
server_word = "server" if server_count == 1 else "servers"
|
|
824
|
+
right_parts.append(f"{server_count} MCP {server_word}")
|
|
825
|
+
|
|
826
|
+
right_info = f"[dim]{' '.join(right_parts)}[/dim]" if right_parts else ""
|
|
827
|
+
|
|
828
|
+
self.display_message(
|
|
829
|
+
content=system_prompt,
|
|
830
|
+
message_type=MessageType.SYSTEM,
|
|
831
|
+
name=agent_name,
|
|
832
|
+
right_info=right_info,
|
|
833
|
+
truncate_content=False, # Don't truncate system prompts
|
|
834
|
+
)
|
|
835
|
+
|
|
819
836
|
async def show_prompt_loaded(
|
|
820
837
|
self,
|
|
821
838
|
prompt_name: str,
|
fast_agent/ui/enhanced_prompt.py
CHANGED
|
@@ -3,12 +3,13 @@ Enhanced prompt functionality with advanced prompt_toolkit features.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import json
|
|
6
7
|
import os
|
|
7
8
|
import shlex
|
|
8
9
|
import subprocess
|
|
9
10
|
import tempfile
|
|
10
11
|
from importlib.metadata import version
|
|
11
|
-
from typing import List, Optional
|
|
12
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
12
13
|
|
|
13
14
|
from prompt_toolkit import PromptSession
|
|
14
15
|
from prompt_toolkit.completion import Completer, Completion, WordCompleter
|
|
@@ -20,9 +21,13 @@ from prompt_toolkit.styles import Style
|
|
|
20
21
|
from rich import print as rich_print
|
|
21
22
|
|
|
22
23
|
from fast_agent.agents.agent_types import AgentType
|
|
24
|
+
from fast_agent.constants import FAST_AGENT_ERROR_CHANNEL, FAST_AGENT_REMOVED_METADATA_CHANNEL
|
|
23
25
|
from fast_agent.core.exceptions import PromptExitError
|
|
24
26
|
from fast_agent.llm.model_info import get_model_info
|
|
25
27
|
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from fast_agent.core.agent_app import AgentApp
|
|
30
|
+
|
|
26
31
|
# Get the application version
|
|
27
32
|
try:
|
|
28
33
|
app_version = version("fast-agent-mcp")
|
|
@@ -38,6 +43,30 @@ available_agents = set()
|
|
|
38
43
|
# Keep track of multi-line mode state
|
|
39
44
|
in_multiline_mode = False
|
|
40
45
|
|
|
46
|
+
|
|
47
|
+
def _extract_alert_flags_from_meta(blocks) -> set[str]:
|
|
48
|
+
flags: set[str] = set()
|
|
49
|
+
for block in blocks or []:
|
|
50
|
+
text = getattr(block, "text", None)
|
|
51
|
+
if not text:
|
|
52
|
+
continue
|
|
53
|
+
try:
|
|
54
|
+
payload = json.loads(text)
|
|
55
|
+
except (TypeError, ValueError):
|
|
56
|
+
continue
|
|
57
|
+
if payload.get("type") != "fast-agent-removed":
|
|
58
|
+
continue
|
|
59
|
+
category = payload.get("category")
|
|
60
|
+
match category:
|
|
61
|
+
case "text":
|
|
62
|
+
flags.add("T")
|
|
63
|
+
case "document":
|
|
64
|
+
flags.add("D")
|
|
65
|
+
case "vision":
|
|
66
|
+
flags.add("V")
|
|
67
|
+
return flags
|
|
68
|
+
|
|
69
|
+
|
|
41
70
|
# Track whether help text has been shown globally
|
|
42
71
|
help_message_shown = False
|
|
43
72
|
|
|
@@ -45,20 +74,17 @@ help_message_shown = False
|
|
|
45
74
|
_agent_info_shown = set()
|
|
46
75
|
|
|
47
76
|
|
|
48
|
-
async def _display_agent_info_helper(agent_name: str, agent_provider:
|
|
77
|
+
async def _display_agent_info_helper(agent_name: str, agent_provider: "AgentApp | None") -> None:
|
|
49
78
|
"""Helper function to display agent information."""
|
|
50
79
|
# Only show once per agent
|
|
51
80
|
if agent_name in _agent_info_shown:
|
|
52
81
|
return
|
|
53
82
|
|
|
54
83
|
try:
|
|
55
|
-
# Get agent info
|
|
56
|
-
if
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
else:
|
|
60
|
-
# This is a single agent
|
|
61
|
-
agent = agent_provider
|
|
84
|
+
# Get agent info from AgentApp
|
|
85
|
+
if agent_provider is None:
|
|
86
|
+
return
|
|
87
|
+
agent = agent_provider._agent(agent_name)
|
|
62
88
|
|
|
63
89
|
# Get counts TODO -- add this to the type library or adjust the way aggregator/reporting works
|
|
64
90
|
server_count = 0
|
|
@@ -148,7 +174,9 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: object) ->
|
|
|
148
174
|
pass
|
|
149
175
|
|
|
150
176
|
|
|
151
|
-
async def _display_all_agents_with_hierarchy(
|
|
177
|
+
async def _display_all_agents_with_hierarchy(
|
|
178
|
+
available_agents: List[str], agent_provider: "AgentApp | None"
|
|
179
|
+
) -> None:
|
|
152
180
|
"""Display all agents with tree structure for workflow agents."""
|
|
153
181
|
# Track which agents are children to avoid displaying them twice
|
|
154
182
|
child_agents = set()
|
|
@@ -156,10 +184,9 @@ async def _display_all_agents_with_hierarchy(available_agents: List[str], agent_
|
|
|
156
184
|
# First pass: identify all child agents
|
|
157
185
|
for agent_name in available_agents:
|
|
158
186
|
try:
|
|
159
|
-
if
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
agent = agent_provider
|
|
187
|
+
if agent_provider is None:
|
|
188
|
+
continue
|
|
189
|
+
agent = agent_provider._agent(agent_name)
|
|
163
190
|
|
|
164
191
|
if agent.agent_type == AgentType.PARALLEL:
|
|
165
192
|
if hasattr(agent, "fan_out_agents") and agent.fan_out_agents:
|
|
@@ -184,10 +211,9 @@ async def _display_all_agents_with_hierarchy(available_agents: List[str], agent_
|
|
|
184
211
|
continue
|
|
185
212
|
|
|
186
213
|
try:
|
|
187
|
-
if
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
agent = agent_provider
|
|
214
|
+
if agent_provider is None:
|
|
215
|
+
continue
|
|
216
|
+
agent = agent_provider._agent(agent_name)
|
|
191
217
|
|
|
192
218
|
# Display parent agent
|
|
193
219
|
await _display_agent_info_helper(agent_name, agent_provider)
|
|
@@ -202,7 +228,7 @@ async def _display_all_agents_with_hierarchy(available_agents: List[str], agent_
|
|
|
202
228
|
continue
|
|
203
229
|
|
|
204
230
|
|
|
205
|
-
async def _display_parallel_children(parallel_agent, agent_provider) -> None:
|
|
231
|
+
async def _display_parallel_children(parallel_agent, agent_provider: "AgentApp | None") -> None:
|
|
206
232
|
"""Display child agents of a parallel agent in tree format."""
|
|
207
233
|
children = []
|
|
208
234
|
|
|
@@ -222,7 +248,7 @@ async def _display_parallel_children(parallel_agent, agent_provider) -> None:
|
|
|
222
248
|
await _display_child_agent_info(child_agent, prefix, agent_provider)
|
|
223
249
|
|
|
224
250
|
|
|
225
|
-
async def _display_router_children(router_agent, agent_provider) -> None:
|
|
251
|
+
async def _display_router_children(router_agent, agent_provider: "AgentApp | None") -> None:
|
|
226
252
|
"""Display child agents of a router agent in tree format."""
|
|
227
253
|
children = []
|
|
228
254
|
|
|
@@ -239,7 +265,9 @@ async def _display_router_children(router_agent, agent_provider) -> None:
|
|
|
239
265
|
await _display_child_agent_info(child_agent, prefix, agent_provider)
|
|
240
266
|
|
|
241
267
|
|
|
242
|
-
async def _display_child_agent_info(
|
|
268
|
+
async def _display_child_agent_info(
|
|
269
|
+
child_agent, prefix: str, agent_provider: "AgentApp | None"
|
|
270
|
+
) -> None:
|
|
243
271
|
"""Display info for a child agent with tree prefix."""
|
|
244
272
|
try:
|
|
245
273
|
# Get counts for child agent
|
|
@@ -297,6 +325,7 @@ class AgentCompleter(Completer):
|
|
|
297
325
|
"tools": "List available MCP tools",
|
|
298
326
|
"prompt": "List and choose MCP prompts, or apply specific prompt (/prompt <name>)",
|
|
299
327
|
"agents": "List available agents",
|
|
328
|
+
"system": "Show the current system prompt",
|
|
300
329
|
"usage": "Show current usage statistics",
|
|
301
330
|
"markdown": "Show last assistant message without markdown formatting",
|
|
302
331
|
"save_history": "Save history; .json = MCP JSON, others = Markdown",
|
|
@@ -424,7 +453,9 @@ def get_text_from_editor(initial_text: str = "") -> str:
|
|
|
424
453
|
return edited_text.strip() # Added strip() to remove trailing newlines often added by editors
|
|
425
454
|
|
|
426
455
|
|
|
427
|
-
def create_keybindings(
|
|
456
|
+
def create_keybindings(
|
|
457
|
+
on_toggle_multiline=None, app=None, agent_provider: "AgentApp | None" = None, agent_name=None
|
|
458
|
+
):
|
|
428
459
|
"""Create custom key bindings."""
|
|
429
460
|
kb = KeyBindings()
|
|
430
461
|
|
|
@@ -503,30 +534,20 @@ def create_keybindings(on_toggle_multiline=None, app=None, agent_provider=None,
|
|
|
503
534
|
"""Ctrl+Y: Copy last assistant response to clipboard."""
|
|
504
535
|
if kb.agent_provider and kb.current_agent_name:
|
|
505
536
|
try:
|
|
506
|
-
# Get the agent
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
import pyperclip
|
|
519
|
-
|
|
520
|
-
pyperclip.copy(content)
|
|
521
|
-
rich_print("\n[green]✓ Copied to clipboard[/green]")
|
|
522
|
-
return
|
|
523
|
-
|
|
524
|
-
else:
|
|
525
|
-
pass
|
|
537
|
+
# Get the agent from AgentApp
|
|
538
|
+
agent = kb.agent_provider._agent(kb.current_agent_name)
|
|
539
|
+
|
|
540
|
+
# Find last assistant message
|
|
541
|
+
for msg in reversed(agent.message_history):
|
|
542
|
+
if msg.role == "assistant":
|
|
543
|
+
content = msg.last_text()
|
|
544
|
+
import pyperclip
|
|
545
|
+
|
|
546
|
+
pyperclip.copy(content)
|
|
547
|
+
rich_print("\n[green]✓ Copied to clipboard[/green]")
|
|
548
|
+
return
|
|
526
549
|
except Exception:
|
|
527
550
|
pass
|
|
528
|
-
else:
|
|
529
|
-
pass
|
|
530
551
|
|
|
531
552
|
return kb
|
|
532
553
|
|
|
@@ -541,7 +562,7 @@ async def get_enhanced_input(
|
|
|
541
562
|
agent_types: dict[str, AgentType] = None,
|
|
542
563
|
is_human_input: bool = False,
|
|
543
564
|
toolbar_color: str = "ansiblue",
|
|
544
|
-
agent_provider:
|
|
565
|
+
agent_provider: "AgentApp | None" = None,
|
|
545
566
|
) -> str:
|
|
546
567
|
"""
|
|
547
568
|
Enhanced input with advanced prompt_toolkit features.
|
|
@@ -556,7 +577,7 @@ async def get_enhanced_input(
|
|
|
556
577
|
agent_types: Dictionary mapping agent names to their types for display
|
|
557
578
|
is_human_input: Whether this is a human input request (disables agent selection features)
|
|
558
579
|
toolbar_color: Color to use for the agent name in the toolbar (default: "ansiblue")
|
|
559
|
-
agent_provider: Optional
|
|
580
|
+
agent_provider: Optional AgentApp for displaying agent info
|
|
560
581
|
|
|
561
582
|
Returns:
|
|
562
583
|
User input string
|
|
@@ -597,18 +618,22 @@ async def get_enhanced_input(
|
|
|
597
618
|
|
|
598
619
|
shortcut_text = " | ".join(f"{key}:{action}" for key, action in shortcuts)
|
|
599
620
|
|
|
600
|
-
# Resolve model name and TDV from the current agent if available
|
|
621
|
+
# Resolve model name, turn counter, and TDV from the current agent if available
|
|
601
622
|
model_display = None
|
|
602
623
|
tdv_segment = None
|
|
624
|
+
turn_count = 0
|
|
603
625
|
try:
|
|
604
|
-
|
|
605
|
-
agent_provider._agent(agent_name)
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
626
|
+
if agent_provider:
|
|
627
|
+
agent = agent_provider._agent(agent_name)
|
|
628
|
+
|
|
629
|
+
# Get turn count from message history
|
|
630
|
+
for message in agent.message_history:
|
|
631
|
+
if message.role == "user":
|
|
632
|
+
turn_count += 1
|
|
633
|
+
|
|
634
|
+
# Get model name from LLM
|
|
635
|
+
if agent.llm and agent.llm.model_name:
|
|
636
|
+
model_name = agent.llm.model_name
|
|
612
637
|
# Truncate model name to max 25 characters with ellipsis
|
|
613
638
|
max_len = 25
|
|
614
639
|
if len(model_name) > max_len:
|
|
@@ -618,20 +643,35 @@ async def get_enhanced_input(
|
|
|
618
643
|
model_display = model_name
|
|
619
644
|
|
|
620
645
|
# Build TDV capability segment based on model database
|
|
621
|
-
info = get_model_info(
|
|
646
|
+
info = get_model_info(agent)
|
|
622
647
|
# Default to text-only if info resolution fails for any reason
|
|
623
648
|
t, d, v = (True, False, False)
|
|
624
649
|
if info:
|
|
625
650
|
t, d, v = info.tdv_flags
|
|
626
651
|
|
|
652
|
+
# Check for alert flags in user messages
|
|
653
|
+
alert_flags: set[str] = set()
|
|
654
|
+
error_seen = False
|
|
655
|
+
for message in agent.message_history:
|
|
656
|
+
if message.channels:
|
|
657
|
+
if message.channels.get(FAST_AGENT_ERROR_CHANNEL):
|
|
658
|
+
error_seen = True
|
|
659
|
+
if message.role == "user" and message.channels:
|
|
660
|
+
meta_blocks = message.channels.get(FAST_AGENT_REMOVED_METADATA_CHANNEL, [])
|
|
661
|
+
alert_flags.update(_extract_alert_flags_from_meta(meta_blocks))
|
|
662
|
+
|
|
663
|
+
if error_seen and not alert_flags:
|
|
664
|
+
alert_flags.add("T")
|
|
665
|
+
|
|
627
666
|
def _style_flag(letter: str, supported: bool) -> str:
|
|
628
667
|
# Enabled uses the same color as NORMAL mode (ansigreen), disabled is dim
|
|
668
|
+
if letter in alert_flags:
|
|
669
|
+
return f"<style fg='ansired' bg='ansiblack'>{letter}</style>"
|
|
670
|
+
|
|
629
671
|
enabled_color = "ansigreen"
|
|
630
|
-
|
|
631
|
-
f"<style fg='{enabled_color}' bg='ansiblack'>{letter}</style>"
|
|
632
|
-
|
|
633
|
-
else f"<style fg='ansiblack' bg='ansiwhite'>{letter}</style>"
|
|
634
|
-
)
|
|
672
|
+
if supported:
|
|
673
|
+
return f"<style fg='{enabled_color}' bg='ansiblack'>{letter}</style>"
|
|
674
|
+
return f"<style fg='ansiblack' bg='ansiwhite'>{letter}</style>"
|
|
635
675
|
|
|
636
676
|
tdv_segment = f"{_style_flag('T', t)}{_style_flag('D', d)}{_style_flag('V', v)}"
|
|
637
677
|
except Exception:
|
|
@@ -639,7 +679,7 @@ async def get_enhanced_input(
|
|
|
639
679
|
model_display = None
|
|
640
680
|
tdv_segment = None
|
|
641
681
|
|
|
642
|
-
# Build dynamic middle segments: model (in green) and optional shortcuts
|
|
682
|
+
# Build dynamic middle segments: model (in green), turn counter, and optional shortcuts
|
|
643
683
|
middle_segments = []
|
|
644
684
|
if model_display:
|
|
645
685
|
# Model chip + inline TDV flags
|
|
@@ -649,6 +689,10 @@ async def get_enhanced_input(
|
|
|
649
689
|
)
|
|
650
690
|
else:
|
|
651
691
|
middle_segments.append(f"<style bg='ansigreen'>{model_display}</style>")
|
|
692
|
+
|
|
693
|
+
# Add turn counter (formatted as 3 digits)
|
|
694
|
+
middle_segments.append(f"{turn_count:03d}")
|
|
695
|
+
|
|
652
696
|
if shortcut_text:
|
|
653
697
|
middle_segments.append(shortcut_text)
|
|
654
698
|
middle = " | ".join(middle_segments)
|
|
@@ -751,6 +795,8 @@ async def get_enhanced_input(
|
|
|
751
795
|
return "CLEAR"
|
|
752
796
|
elif cmd == "agents":
|
|
753
797
|
return "LIST_AGENTS"
|
|
798
|
+
elif cmd == "system":
|
|
799
|
+
return "SHOW_SYSTEM"
|
|
754
800
|
elif cmd == "usage":
|
|
755
801
|
return "SHOW_USAGE"
|
|
756
802
|
elif cmd == "markdown":
|
|
@@ -758,7 +804,9 @@ async def get_enhanced_input(
|
|
|
758
804
|
elif cmd in ("save_history", "save"):
|
|
759
805
|
# Return a structured action for the interactive loop to handle
|
|
760
806
|
# Prefer programmatic saving via HistoryExporter; fall back to magic-string there if needed
|
|
761
|
-
filename =
|
|
807
|
+
filename = (
|
|
808
|
+
cmd_parts[1].strip() if len(cmd_parts) > 1 and cmd_parts[1].strip() else None
|
|
809
|
+
)
|
|
762
810
|
return {"save_history": True, "filename": filename}
|
|
763
811
|
elif cmd == "prompt":
|
|
764
812
|
# Handle /prompt with no arguments as interactive mode
|
|
@@ -933,11 +981,14 @@ async def handle_special_commands(command, agent_app=None):
|
|
|
933
981
|
rich_print(" /help - Show this help")
|
|
934
982
|
rich_print(" /clear - Clear screen")
|
|
935
983
|
rich_print(" /agents - List available agents")
|
|
984
|
+
rich_print(" /system - Show the current system prompt")
|
|
936
985
|
rich_print(" /prompt <name> - Apply a specific prompt by name")
|
|
937
986
|
rich_print(" /usage - Show current usage statistics")
|
|
938
987
|
rich_print(" /markdown - Show last assistant message without markdown formatting")
|
|
939
988
|
rich_print(" /save_history <filename> - Save current chat history to a file")
|
|
940
|
-
rich_print(
|
|
989
|
+
rich_print(
|
|
990
|
+
" [dim]Tip: Use a .json extension for MCP-compatible JSON; any other extension saves Markdown.[/dim]"
|
|
991
|
+
)
|
|
941
992
|
rich_print(" @agent_name - Switch to agent")
|
|
942
993
|
rich_print(" STOP - Return control back to the workflow")
|
|
943
994
|
rich_print(" EXIT - Exit fast-agent, terminating any running workflows")
|
|
@@ -972,6 +1023,10 @@ async def handle_special_commands(command, agent_app=None):
|
|
|
972
1023
|
# Return a dictionary to signal that usage should be shown
|
|
973
1024
|
return {"show_usage": True}
|
|
974
1025
|
|
|
1026
|
+
elif command == "SHOW_SYSTEM":
|
|
1027
|
+
# Return a dictionary to signal that system prompt should be shown
|
|
1028
|
+
return {"show_system": True}
|
|
1029
|
+
|
|
975
1030
|
elif command == "MARKDOWN":
|
|
976
1031
|
# Return a dictionary to signal that markdown display should be shown
|
|
977
1032
|
return {"show_markdown": True}
|