fix(cron): keep SOUL.md identity when workdir is unset

This commit is contained in:
刘昊
2026-04-28 17:51:57 +08:00
committed by Teknium
parent 0a5ee01e48
commit 60c6b07128
4 changed files with 38 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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