Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/claw/hermes_base_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ func TestHermesBaseImageSourceContract(t *testing.T) {
`Matching tolerates case, whitespace, and punctuation`,
`os.chmod(real_path, 0o666)`,
`HERMES_CRON_DELIVER_TRANSIENT_FAILURES`,
`cron scheduler protected toolsets documentation`,
`cron scheduler memory tool suppression`,
`["cronjob", "messaging", "clarify", "memory"]`,
`_claw_should_deliver_cron_failure`,
`upstream request failed`,
`suppressing transient cron failure delivery`,
Expand Down
17 changes: 17 additions & 0 deletions cmd/claw/hermes_base_spike_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ assert importlib.util.find_spec("minisweagent_path") is not None
import tools.terminal_tool

from cron import scheduler as cron_scheduler
from model_tools import get_tool_definitions
cron_disabled = cron_scheduler._resolve_cron_disabled_toolsets({})
assert "memory" in cron_disabled
cron_tools = {
tool["function"]["name"]
for tool in get_tool_definitions(disabled_toolsets=cron_disabled, quiet_mode=True)
}
assert "memory" not in cron_tools
interactive_tools = {
tool["function"]["name"]
for tool in get_tool_definitions(
disabled_toolsets=["cronjob", "messaging", "clarify"],
quiet_mode=True,
)
}
assert "memory" in interactive_tools
assert not cron_scheduler._claw_should_deliver_cron_failure("upstream request failed")
assert not cron_scheduler._claw_should_deliver_cron_failure("Internal Server Error")
assert cron_scheduler._claw_should_deliver_cron_failure("prompt injection scanner blocked the job")
Expand All @@ -61,6 +77,7 @@ assert str(_get_lock_dir()) == "/tmp/hermes-gateway-locks"

from toolsets import _HERMES_CORE_TOOLS, TOOLSETS
assert "text_to_speech" not in _HERMES_CORE_TOOLS
assert "memory" in _HERMES_CORE_TOOLS
for _name, _toolset in TOOLSETS.items():
if isinstance(_toolset, dict) and isinstance(_toolset.get("tools"), list):
assert "text_to_speech" not in _toolset["tools"], _name
Expand Down
24 changes: 24 additions & 0 deletions dockerfiles/hermes-base/patch-hermes-runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,30 @@ def _atomic_write_text(file_path: Path, content: str, encoding: str = "utf-8") -
cron_scheduler = purelib / "cron" / "scheduler.py"
text = cron_scheduler.read_text()

# Cron jobs initialize Hermes with skip_memory=True so their system prompts do
# not mutate user representations. Keep the advertised tools consistent with
# that runtime capability while leaving native memory available to interactive
# agents.
text = replace_once(
text,
" Three protected toolsets are always disabled in cron context:\n"
" - ``cronjob`` — would let a cron-spawned agent schedule more cron jobs\n"
" - ``messaging`` — interactive, needs a live gateway session\n"
" - ``clarify`` — interactive, blocks waiting for user input\n",
" Four protected toolsets are always disabled in cron context:\n"
" - ``cronjob`` — would let a cron-spawned agent schedule more cron jobs\n"
" - ``messaging`` — interactive, needs a live gateway session\n"
" - ``clarify`` — interactive, blocks waiting for user input\n"
" - ``memory`` — unavailable because cron agents use ``skip_memory=True``\n",
"cron scheduler protected toolsets documentation",
)
text = replace_once(
text,
' disabled = ["cronjob", "messaging", "clarify"]\n',
' disabled = ["cronjob", "messaging", "clarify", "memory"]\n',
"cron scheduler memory tool suppression",
)

# Cron transient-failure delivery: provider/cllama outages are already logged
# and recorded on the job run. They should not be posted into user channels as
# actionable cron responses, while real operator-actionable cron failures still
Expand Down
Loading