Skip to content

Commit 15c9f0b

Browse files
authored
kimi 2.6 (#775)
* kimi 2.6 agent trace uploads * Relax brittle internal prompt unit test * improve internal slash command help * fix review issues (agent name etc.) * fix issues, version up, hf dep * serialize zero-arg tool calls as {} * timestamp fixes * Fix session export session lookup and relax kimi alias test * Fix ACP implicit session export lookup
1 parent c72f454 commit 15c9f0b

63 files changed

Lines changed: 5312 additions & 202 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "fast-agent-mcp"
3-
version = "0.6.19"
3+
version = "0.6.20"
44
description = "Define, Prompt and Test MCP enabled Agents and Workflows"
55
readme = "README.md"
66
license = { file = "LICENSE" }
@@ -46,6 +46,7 @@ dependencies = [
4646
"uvloop==0.22.1; platform_system != 'Windows'",
4747
"multilspy==0.0.15",
4848
"ruamel.yaml==0.19.1",
49+
"huggingface_hub==1.9.0",
4950
"mslex==1.3.0",
5051
]
5152

@@ -116,7 +117,6 @@ testpaths = ["tests"]
116117
[dependency-groups]
117118
dev = [
118119
"boto3>=1.35.0",
119-
"huggingface_hub==1.9.0",
120120
"multilspy>=0.0.15",
121121
"pre-commit>=4.0.1",
122122
"pydantic>=2.10.4",

resources/shared/smart_prompt.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
You are a helpful AI Agent.
1+
You are a helpful AI Agent running in the `fast-agent` harness.
22

33
You have the ability to create sub-agents and delegate tasks to them.
44

5-
Information about how to do so is below. Pre-existing cards may be in the `fast-agent environment` directories. You may issue
6-
multiple calls in parallel to new or existing AgentCard definitions.
5+
Information about how to do so is below. Pre-existing cards may be in the `fast-agent environment` directories. You may issue multiple calls in parallel to new or existing AgentCard definitions.
76

87
{{agentInternalResources}}
98

@@ -13,11 +12,13 @@ multiple calls in parallel to new or existing AgentCard definitions.
1312
{{env}}
1413

1514
fast-agent environment paths:
15+
1616
- Environment root: {{environmentDir}}
1717
- Agent cards: {{environmentAgentCardsDir}}
1818
- Tool cards: {{environmentToolCardsDir}}
1919

2020
Current agent identity:
21+
2122
- Name: {{agentName}}
2223
- Type: {{agentType}}
2324
- AgentCard path: {{agentCardPath}}
@@ -42,4 +43,6 @@ declare them with `mcp_connect` entries (`target` + optional `name`). Prefer exp
4243
`defer_loading: true` automatically enables server-side `tool_search` for lazy
4344
remote tool or connector loading.
4445

46+
Mermaid diagrams between code fences are supported.
47+
4548
The current date is {{currentDate}}.

src/fast_agent/acp/slash/handlers/cards_manager.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from typing import TYPE_CHECKING, cast
66

7+
from fast_agent.commands.command_discovery import render_direct_command_help
78
from fast_agent.commands.handlers import cards_manager as cards_handlers
89

910
if TYPE_CHECKING:
@@ -12,6 +13,10 @@
1213

1314

1415
async def handle_cards(handler: "SlashCommandHandler", arguments: str | None = None) -> str:
16+
direct_help = render_direct_command_help("cards", arguments)
17+
if direct_help is not None:
18+
return direct_help
19+
1520
tokens = (arguments or "").strip().split(maxsplit=1)
1621
action = tokens[0].lower() if tokens else "list"
1722
remainder = tokens[1] if len(tokens) > 1 else ""

src/fast_agent/acp/slash/handlers/commands.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ async def handle_commands(handler: "SlashCommandHandler", arguments: str | None
3535
if request.as_json:
3636
return render_commands_json(
3737
command_name=request.command_name,
38+
action_name=request.action_name,
3839
command_names=available_names,
3940
)
4041

@@ -51,8 +52,14 @@ async def handle_commands(handler: "SlashCommandHandler", arguments: str | None
5152
f"Use `/commands` to list available commands.{suggestion_line}"
5253
)
5354

54-
detail = render_command_detail_markdown(request.command_name)
55+
detail = render_command_detail_markdown(request.command_name, request.action_name)
5556
if detail is not None:
5657
return detail
5758

59+
if request.action_name is not None:
60+
return (
61+
f"# commands\n\nNo discovery metadata for `/{request.command_name} "
62+
f"{request.action_name}` yet."
63+
)
64+
5865
return f"# commands\n\nNo discovery metadata for `{request.command_name}` yet."

src/fast_agent/acp/slash/handlers/model.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import TYPE_CHECKING, cast
77

88
from fast_agent.acp.command_io import ACPCommandIO
9+
from fast_agent.commands.command_discovery import render_direct_command_help
910
from fast_agent.commands.context import CommandContext, StaticAgentProvider
1011
from fast_agent.commands.handlers import model as model_handlers
1112
from fast_agent.commands.handlers import models_manager as models_manager_handlers
@@ -27,6 +28,10 @@ async def _handle_model_like(
2728
*,
2829
heading_prefix: str,
2930
) -> str:
31+
direct_help = render_direct_command_help(heading_prefix, arguments)
32+
if direct_help is not None:
33+
return direct_help
34+
3035
remainder = (arguments or "").strip()
3136
value = None
3237
command_kind = "reasoning" if heading_prefix == "model" else "doctor"

src/fast_agent/acp/slash/handlers/session.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@
44

55
from typing import TYPE_CHECKING, cast
66

7+
from fast_agent.commands.handlers import session_export as session_export_handlers
78
from fast_agent.commands.handlers import sessions as sessions_handlers
89
from fast_agent.commands.handlers.shared import clear_agent_histories
910
from fast_agent.commands.renderers.session_markdown import render_session_list_markdown
11+
from fast_agent.commands.results import CommandOutcome
12+
from fast_agent.commands.session_export_help import render_session_export_help_markdown
1013
from fast_agent.commands.session_summaries import build_session_list_summary
11-
from fast_agent.commands.shared_command_intents import parse_session_command_intent
14+
from fast_agent.commands.shared_command_intents import (
15+
parse_session_command_intent,
16+
should_default_export_agent,
17+
)
1218

1319
if TYPE_CHECKING:
1420
from fast_agent.acp.command_io import ACPCommandIO
@@ -43,13 +49,15 @@ async def handle_session(handler: "SlashCommandHandler", arguments: str | None =
4349
return await handle_session_delete(handler, intent.argument)
4450
if intent.action == "pin":
4551
return await handle_session_pin(handler, value=intent.pin_value, target=intent.pin_target)
52+
if intent.action == "export":
53+
return await handle_session_export(handler, intent)
4654

4755
return "\n".join(
4856
[
4957
"# session",
5058
"",
5159
f"Unknown /session action: {intent.raw_subcommand or ''}",
52-
"Usage: /session [list|new|resume|title|fork|delete|pin] [args]",
60+
"Usage: /session [list|new|resume|title|fork|delete|pin|export] [args]",
5361
]
5462
)
5563

@@ -153,3 +161,45 @@ async def handle_session_pin(
153161
target=target,
154162
)
155163
return handler._format_outcome_as_markdown(outcome, "session pin", io=io)
164+
165+
166+
async def handle_session_export(handler: "SlashCommandHandler", intent) -> str:
167+
if intent.export_help:
168+
return render_session_export_help_markdown()
169+
170+
ctx = handler._build_command_context()
171+
io = cast("ACPCommandIO", ctx.io)
172+
manager = ctx.resolve_session_manager()
173+
current_session = manager.current_session
174+
current_session_id = current_session.info.name if current_session is not None else None
175+
if current_session_id != handler.session_id:
176+
try:
177+
handler_session = manager.get_session(handler.session_id)
178+
except AttributeError:
179+
handler_session = None
180+
current_session_id = handler_session.info.name if handler_session is not None else None
181+
if intent.export_target is None and current_session_id is None:
182+
outcome = CommandOutcome()
183+
outcome.add_message(
184+
"No active session to export.",
185+
channel="error",
186+
right_info="session",
187+
)
188+
return handler._format_outcome_as_markdown(outcome, "session export", io=io)
189+
agent_name = intent.export_agent
190+
if agent_name is None and should_default_export_agent(
191+
intent.export_target,
192+
current_session_id=current_session_id,
193+
):
194+
agent_name = handler.current_agent_name
195+
outcome = await session_export_handlers.handle_session_export(
196+
ctx,
197+
target=intent.export_target,
198+
agent_name=agent_name,
199+
output_path=intent.export_output,
200+
hf_dataset=intent.export_hf_dataset,
201+
hf_dataset_path=intent.export_hf_dataset_path,
202+
current_session_id=current_session_id,
203+
error=intent.export_error,
204+
)
205+
return handler._format_outcome_as_markdown(outcome, "session export", io=io)

src/fast_agent/acp/slash/handlers/skills.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ToolCallStart,
1616
)
1717

18+
from fast_agent.commands.command_discovery import render_direct_command_help
1819
from fast_agent.commands.handlers import skills as skills_handlers
1920
from fast_agent.commands.renderers.skills_markdown import (
2021
render_marketplace_skills,
@@ -112,6 +113,10 @@ async def handle_skills_available(
112113

113114

114115
async def handle_skills(handler: "SlashCommandHandler", arguments: str | None = None) -> str:
116+
direct_help = render_direct_command_help("skills", arguments)
117+
if direct_help is not None:
118+
return direct_help
119+
115120
tokens = (arguments or "").strip().split(maxsplit=1)
116121
action = tokens[0].lower() if tokens else "list"
117122
remainder = tokens[1] if len(tokens) > 1 else ""

src/fast_agent/acp/slash_commands.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def __init__(
216216
self._acp_context: ACPContext | None = None
217217

218218
cards_action_hint = "|".join(
219-
action for action in command_action_names("cards") if action != "list"
219+
action for action in command_action_names("cards") if action not in {"list", "readme", "help"}
220220
) or "add|remove|update|publish|registry"
221221

222222
# Session-level commands (always available, operate on current agent)
@@ -294,7 +294,7 @@ def __init__(
294294
description="List or manage sessions",
295295
input=AvailableCommandInput(
296296
root=UnstructuredCommandInput(
297-
hint="[list|new|resume|title|fork|clear] [args]"
297+
hint="[list|new|resume|title|fork|delete|pin|export] [args]"
298298
)
299299
),
300300
),

src/fast_agent/agents/llm_decorator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from collections import Counter, defaultdict
88
from copy import deepcopy
99
from dataclasses import dataclass
10+
from datetime import datetime, timezone
1011
from pathlib import Path
1112
from typing import (
1213
TYPE_CHECKING,
@@ -847,6 +848,7 @@ def _prepare_llm_call(
847848
) -> _CallContext:
848849
"""Normalize template/history handling for both generate and structured."""
849850
sanitized_messages, summary = self._sanitize_messages_for_llm(messages)
851+
self._timestamp_messages(sanitized_messages)
850852
final_request_params = self._require_llm().get_request_params(request_params)
851853

852854
use_history = final_request_params.use_history if final_request_params else True
@@ -916,9 +918,17 @@ def _persist_history(
916918
return
917919

918920
history_messages = [self._strip_removed_metadata(msg) for msg in sanitized_messages]
921+
assistant_message.ensure_timestamp()
919922
self._message_history.extend(history_messages)
920923
self._message_history.append(assistant_message)
921924

925+
@staticmethod
926+
def _timestamp_messages(messages: list[PromptMessageExtended]) -> None:
927+
"""Attach UTC wall-clock timestamps to new turn messages before LLM execution."""
928+
for message in messages:
929+
if message.timestamp is None:
930+
message.timestamp = datetime.now(timezone.utc)
931+
922932
@staticmethod
923933
def _strip_removed_metadata(message: PromptMessageExtended) -> PromptMessageExtended:
924934
"""Remove per-turn removed-content metadata before persisting to history."""

0 commit comments

Comments
 (0)