fix(cron): keep SOUL.md identity when workdir is unset
This commit is contained in:
@@ -1033,10 +1033,12 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
|
||||
enabled_toolsets=_resolve_cron_enabled_toolsets(job, _cfg),
|
||||
disabled_toolsets=["cronjob", "messaging", "clarify"],
|
||||
quiet_mode=True,
|
||||
# When a workdir is configured, inject AGENTS.md / CLAUDE.md /
|
||||
# .cursorrules from that directory; otherwise preserve the old
|
||||
# behaviour (don't inject SOUL.md/AGENTS.md from the scheduler cwd).
|
||||
# Cron jobs should always inherit the user's SOUL.md identity from
|
||||
# HERMES_HOME. When a workdir is configured, also inject project
|
||||
# context files (AGENTS.md / CLAUDE.md / .cursorrules) from there.
|
||||
# Without a workdir, keep cwd context discovery disabled.
|
||||
skip_context_files=not bool(_job_workdir),
|
||||
load_soul_identity=True,
|
||||
skip_memory=True, # Cron system prompts would corrupt user representations
|
||||
platform="cron",
|
||||
session_id=_cron_session_id,
|
||||
|
||||
11
run_agent.py
11
run_agent.py
@@ -926,6 +926,7 @@ class AIAgent:
|
||||
thread_id: str = None,
|
||||
gateway_session_key: str = None,
|
||||
skip_context_files: bool = False,
|
||||
load_soul_identity: bool = False,
|
||||
skip_memory: bool = False,
|
||||
session_db=None,
|
||||
parent_session_id: str = None,
|
||||
@@ -977,6 +978,9 @@ class AIAgent:
|
||||
skip_context_files (bool): If True, skip auto-injection of SOUL.md, AGENTS.md, and .cursorrules
|
||||
into the system prompt. Use this for batch processing and data generation to avoid
|
||||
polluting trajectories with user-specific persona or project instructions.
|
||||
load_soul_identity (bool): If True, still use ~/.hermes/SOUL.md as the primary
|
||||
identity even when skip_context_files=True. Project context files from the cwd
|
||||
remain skipped.
|
||||
"""
|
||||
_install_safe_stdio()
|
||||
|
||||
@@ -1005,6 +1009,7 @@ class AIAgent:
|
||||
self._print_fn = None
|
||||
self.background_review_callback = None # Optional sync callback for gateway delivery
|
||||
self.skip_context_files = skip_context_files
|
||||
self.load_soul_identity = load_soul_identity
|
||||
self.pass_session_id = pass_session_id
|
||||
self._credential_pool = credential_pool
|
||||
self.log_prefix_chars = log_prefix_chars
|
||||
@@ -4742,9 +4747,11 @@ class AIAgent:
|
||||
# 6. Current date & time (frozen at build time)
|
||||
# 7. Platform-specific formatting hint
|
||||
|
||||
# Try SOUL.md as primary identity (unless context files are skipped)
|
||||
# Try SOUL.md as primary identity unless the caller explicitly skipped it.
|
||||
# Some execution modes (cron) still want HERMES_HOME persona while keeping
|
||||
# cwd project instructions disabled.
|
||||
_soul_loaded = False
|
||||
if not self.skip_context_files:
|
||||
if self.load_soul_identity or not self.skip_context_files:
|
||||
_soul_content = load_soul_md()
|
||||
if _soul_content:
|
||||
prompt_parts = [_soul_content]
|
||||
|
||||
@@ -265,6 +265,7 @@ class TestRunJobTerminalCwd:
|
||||
class FakeAgent:
|
||||
def __init__(self, **kwargs):
|
||||
observed["skip_context_files"] = kwargs.get("skip_context_files")
|
||||
observed["load_soul_identity"] = kwargs.get("load_soul_identity")
|
||||
observed["terminal_cwd_during_init"] = os.environ.get(
|
||||
"TERMINAL_CWD", "_UNSET_"
|
||||
)
|
||||
@@ -335,6 +336,7 @@ class TestRunJobTerminalCwd:
|
||||
|
||||
# AIAgent was built with skip_context_files=False (feature ON).
|
||||
assert observed["skip_context_files"] is False
|
||||
assert observed["load_soul_identity"] is True
|
||||
# TERMINAL_CWD was pointing at the job workdir while the agent ran.
|
||||
assert observed["terminal_cwd_during_init"] == str(tmp_path.resolve())
|
||||
assert observed["terminal_cwd_during_run"] == str(tmp_path.resolve())
|
||||
@@ -373,6 +375,8 @@ class TestRunJobTerminalCwd:
|
||||
|
||||
# Feature is OFF — skip_context_files stays True.
|
||||
assert observed["skip_context_files"] is True
|
||||
# Cron still forces SOUL.md identity even when cwd context files stay off.
|
||||
assert observed["load_soul_identity"] is True
|
||||
# TERMINAL_CWD saw the same value during init as it had before.
|
||||
assert observed["terminal_cwd_during_init"] == before
|
||||
# And after run_job completes, it's still the sentinel (nothing
|
||||
|
||||
@@ -862,6 +862,26 @@ class TestBuildSystemPrompt:
|
||||
prompt = agent._build_system_prompt()
|
||||
assert DEFAULT_AGENT_IDENTITY in prompt
|
||||
|
||||
def test_can_use_soul_identity_even_when_context_files_are_skipped(self):
|
||||
with (
|
||||
patch("run_agent.get_tool_definitions", return_value=_make_tool_defs("terminal")),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
patch("run_agent.OpenAI"),
|
||||
patch("run_agent.load_soul_md", return_value="SOUL IDENTITY"),
|
||||
):
|
||||
agent = AIAgent(
|
||||
api_key="test-k...7890",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
quiet_mode=True,
|
||||
skip_context_files=True,
|
||||
load_soul_identity=True,
|
||||
skip_memory=True,
|
||||
)
|
||||
prompt = agent._build_system_prompt()
|
||||
|
||||
assert "SOUL IDENTITY" in prompt
|
||||
assert DEFAULT_AGENT_IDENTITY not in prompt
|
||||
|
||||
def test_includes_system_message(self, agent):
|
||||
prompt = agent._build_system_prompt(system_message="Custom instruction")
|
||||
assert "Custom instruction" in prompt
|
||||
|
||||
Reference in New Issue
Block a user