fix(agent): expose session timestamps in model context

Include persisted turn timestamps when assembling LLM prompts so relative-date references like yesterday and today have concrete anchors.

Made-with: Cursor
This commit is contained in:
Xubin Ren 2026-04-26 17:42:58 +00:00
parent c64ec3e73c
commit df37a36174
5 changed files with 67 additions and 5 deletions

View File

@ -832,7 +832,7 @@ class AgentLoop:
if is_subagent and self._persist_subagent_followup(session, msg):
self.sessions.save(session)
self._set_tool_context(channel, chat_id, msg.metadata.get("message_id"))
history = session.get_history(max_messages=0)
history = session.get_history(max_messages=0, include_timestamps=True)
current_role = "assistant" if is_subagent else "user"
# Subagent content is already in `history` above; passing it again
@ -901,7 +901,7 @@ class AgentLoop:
if isinstance(message_tool, MessageTool):
message_tool.start_turn()
history = session.get_history(max_messages=0)
history = session.get_history(max_messages=0, include_timestamps=True)
pending_ask_id = pending_ask_user_id(history)
if pending_ask_id:

View File

@ -494,7 +494,7 @@ class Consolidator:
session_summary: str | None = None,
) -> tuple[int, str]:
"""Estimate current prompt size for the normal session history view."""
history = session.get_history(max_messages=0)
history = session.get_history(max_messages=0, include_timestamps=True)
channel, chat_id = (session.key.split(":", 1) if ":" in session.key else (None, None))
probe_messages = self._build_messages(
history=history,

View File

@ -30,6 +30,18 @@ class Session:
metadata: dict[str, Any] = field(default_factory=dict)
last_consolidated: int = 0 # Number of messages already consolidated to files
@staticmethod
def _annotate_message_time(message: dict[str, Any], content: Any) -> Any:
"""Expose persisted turn timestamps to the model for relative-date reasoning."""
timestamp = message.get("timestamp")
if (
not timestamp
or message.get("role") not in {"user", "assistant"}
or not isinstance(content, str)
):
return content
return f"[Message Time: {timestamp}]\n{content}"
def add_message(self, role: str, content: str, **kwargs: Any) -> None:
"""Add a message to the session."""
msg = {
@ -41,7 +53,12 @@ class Session:
self.messages.append(msg)
self.updated_at = datetime.now()
def get_history(self, max_messages: int = 500) -> list[dict[str, Any]]:
def get_history(
self,
max_messages: int = 500,
*,
include_timestamps: bool = False,
) -> list[dict[str, Any]]:
"""Return unconsolidated messages for LLM input, aligned to a legal tool-call boundary."""
unconsolidated = self.messages[self.last_consolidated:]
sliced = unconsolidated[-max_messages:]
@ -75,6 +92,8 @@ class Session:
image_placeholder_text(p) for p in media if isinstance(p, str) and p
)
content = f"{content}\n{breadcrumbs}" if content else breadcrumbs
if include_timestamps:
content = self._annotate_message_time(message, content)
entry: dict[str, Any] = {"role": message["role"], "content": content}
for key in ("tool_calls", "tool_call_id", "name", "reasoning_content"):
if key in message:

View File

@ -535,7 +535,10 @@ async def test_system_subagent_followup_is_persisted_before_prompt_assembly(tmp_
)
non_system = [m for m in seen["initial_messages"] if m.get("role") != "system"]
assert [m["content"] for m in non_system[:2]] == ["question", "working"]
assert "question" in non_system[0]["content"]
assert "working" in non_system[1]["content"]
assert "[Message Time:" in non_system[0]["content"]
assert "[Message Time:" in non_system[1]["content"]
assert non_system[2]["content"].count("subagent result") == 1
assert "Current Time:" in non_system[2]["content"]

View File

@ -194,6 +194,46 @@ def test_get_history_preserves_reasoning_content():
]
def test_get_history_exposes_turn_timestamps_to_model():
session = Session(key="test:timestamps")
session.messages.append({
"role": "user",
"content": "10 点提醒是昨天发生的",
"timestamp": "2026-04-26T22:00:00",
})
session.messages.append({
"role": "assistant",
"content": "记下来了",
"timestamp": "2026-04-26T22:00:05",
})
history = session.get_history(max_messages=500, include_timestamps=True)
assert history == [
{
"role": "user",
"content": "[Message Time: 2026-04-26T22:00:00]\n10 点提醒是昨天发生的",
},
{
"role": "assistant",
"content": "[Message Time: 2026-04-26T22:00:05]\n记下来了",
},
]
def test_get_history_does_not_annotate_tool_results_with_timestamps():
session = Session(key="test:tool-timestamps")
session.messages.append({"role": "user", "content": "run tool"})
session.messages.extend(_tool_turn("ts", 0))
session.messages[-1]["timestamp"] = "2026-04-26T22:00:10"
history = session.get_history(max_messages=500, include_timestamps=True)
tool_result = history[-1]
assert tool_result["role"] == "tool"
assert tool_result["content"] == "ok"
# --- Window cuts mid-group: assistant present but some tool results orphaned ---
def test_window_cuts_mid_tool_group():