fix(conversation): use resolve_agent_cwd() and Platform line for stored-prompt staleness check

This commit is contained in:
MorAlekss 2026-07-15 06:05:47 -07:00 committed by Teknium
parent 30cd6e989d
commit 7a61b66dac
3 changed files with 63 additions and 14 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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"
)