mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-26 17:38:36 +00:00
fix(conversation): use resolve_agent_cwd() and Platform line for stored-prompt staleness check
This commit is contained in:
parent
30cd6e989d
commit
7a61b66dac
3 changed files with 63 additions and 14 deletions
|
|
@ -48,6 +48,7 @@ from agent.turn_context import (
|
|||
reanchor_current_turn_user_idx,
|
||||
)
|
||||
from agent.turn_retry_state import TurnRetryState
|
||||
from agent.runtime_cwd import resolve_agent_cwd
|
||||
from agent.message_sanitization import (
|
||||
close_interrupted_tool_sequence,
|
||||
_repair_tool_call_arguments,
|
||||
|
|
@ -561,16 +562,20 @@ def _stored_prompt_matches_runtime(agent, prompt: str) -> bool:
|
|||
|
||||
# Detect cwd drift: if the stored prompt was built in a different working
|
||||
# directory, reuse would silently inject a stale path into the prefix cache.
|
||||
# Compare against resolve_agent_cwd() — the SAME resolver used to build the
|
||||
# prompt — so gateway/TUI sessions that set TERMINAL_CWD are not falsely
|
||||
# rejected (they would always differ from the launch dir's os.getcwd()).
|
||||
stored_cwd = line_value("Current working directory")
|
||||
if stored_cwd:
|
||||
import os as _os
|
||||
if stored_cwd != _os.getcwd():
|
||||
if stored_cwd != str(resolve_agent_cwd()):
|
||||
return False
|
||||
|
||||
# Detect runtime surface drift: desktop GUI vs non-desktop.
|
||||
has_desktop_marker = "Runtime surface: you're running inside the Hermes desktop GUI app." in prompt
|
||||
in_desktop = (os.environ.get("HERMES_DESKTOP", "").strip().lower() in {"1", "true", "yes"})
|
||||
if has_desktop_marker != in_desktop:
|
||||
# Detect runtime-surface drift: the stored prompt records which platform it
|
||||
# was built for (e.g. "desktop" vs "cli"). Reusing a desktop-built prompt on
|
||||
# a terminal session (or vice versa) would inject the wrong runtime hints.
|
||||
stored_platform = line_value("Platform")
|
||||
current_platform = str(getattr(agent, "platform", "") or "").strip()
|
||||
if stored_platform and current_platform and stored_platform != current_platform:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -532,6 +532,8 @@ def build_system_prompt_parts(agent: Any, system_message: Optional[str] = None)
|
|||
timestamp_line += f"\nModel: {agent.model}"
|
||||
if agent.provider:
|
||||
timestamp_line += f"\nProvider: {agent.provider}"
|
||||
if agent.platform:
|
||||
timestamp_line += f"\nPlatform: {agent.platform}"
|
||||
volatile_parts.append(timestamp_line)
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -487,19 +487,61 @@ class TestStoredPromptCwdDrift:
|
|||
)
|
||||
|
||||
def test_stored_prompt_stale_when_runtime_surface_differs(self):
|
||||
"""Desktop GUI marker in stored prompt must not be reused when running in terminal."""
|
||||
from unittest.mock import patch
|
||||
"""A stored prompt built for a different platform must not be reused."""
|
||||
from agent.conversation_loop import _stored_prompt_matches_runtime
|
||||
|
||||
agent = self._make_agent()
|
||||
stored_prompt = (
|
||||
"Runtime surface: you're running inside the Hermes desktop GUI app.\n"
|
||||
"Platform: desktop\n"
|
||||
"Model: test/model\n"
|
||||
"Provider: openrouter\n"
|
||||
)
|
||||
agent = self._make_agent()
|
||||
agent.platform = "cli"
|
||||
assert _stored_prompt_matches_runtime(agent, stored_prompt) is False, (
|
||||
"Expected False when stored prompt is for 'desktop' but the "
|
||||
"current session runs on 'cli'"
|
||||
)
|
||||
|
||||
# Current runtime is terminal/CLI: no desktop marker.
|
||||
with patch.dict(os.environ, {"HERMES_DESKTOP": ""}, clear=False):
|
||||
assert _stored_prompt_matches_runtime(agent, stored_prompt) is False, (
|
||||
"Expected False when stored prompt claims desktop but HERMES_DESKTOP is unset"
|
||||
def test_stored_prompt_fresh_when_platform_matches(self):
|
||||
"""Matching platform allows prompt reuse."""
|
||||
from agent.conversation_loop import _stored_prompt_matches_runtime
|
||||
|
||||
agent = self._make_agent()
|
||||
agent.platform = "desktop"
|
||||
stored_prompt = (
|
||||
"Platform: desktop\n"
|
||||
"Model: test/model\n"
|
||||
"Provider: openrouter\n"
|
||||
)
|
||||
assert _stored_prompt_matches_runtime(agent, stored_prompt) is True, (
|
||||
"Expected True when stored platform matches the current platform"
|
||||
)
|
||||
|
||||
def test_built_prompt_contains_platform_line(self):
|
||||
"""The built system prompt must carry a Platform: line so drift detection works."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
from hermes_state import SessionDB
|
||||
from run_agent import AIAgent
|
||||
from agent.system_prompt import build_system_prompt_parts
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
db = SessionDB(db_path=Path(tmpdir) / "test.db")
|
||||
with patch.dict(os.environ, {"OPENROUTER_API_KEY": "test-key"}):
|
||||
agent = AIAgent(
|
||||
api_key="test-key",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
model="test/model",
|
||||
provider="openrouter",
|
||||
quiet_mode=True,
|
||||
session_db=db,
|
||||
session_id="platform-test",
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
)
|
||||
agent.platform = "cli"
|
||||
parts = build_system_prompt_parts(agent)
|
||||
assert "Platform: cli" in parts["volatile"], (
|
||||
"Built prompt missing 'Platform: cli' — drift detection cannot read it"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue