Skip to content

Commit b4394b8

Browse files
authored
fix(hermes): bound channel turns and suppress Telegram progress spam (#43)
1 parent 0f211fd commit b4394b8

4 files changed

Lines changed: 67 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ captures *what shipped* and the *why* — the equivalent of a good commit body,
77
collected here so operators don't have to spelunk `git log`. Tag-style
88
versions are optional; date-stamped sections are fine for in-flight work.
99

10+
## 2026-07-21
11+
12+
### Hermes messaging loop protection
13+
14+
Telegram now delivers only the final assistant response: token streaming,
15+
tool-progress bubbles, and interim assistant commentary are disabled for that
16+
surface, while long-running heartbeat notifications remain enabled. Hermes
17+
agent requests also receive a configurable `PERKOS_AGENT_MAX_TURNS` budget
18+
(default `30`, validated as a positive integer) instead of inheriting the
19+
upstream 90-turn default. This bounds malformed provider/tool combinations and
20+
prevents permanent Telegram progress-message spam. The guard was added after
21+
an invalid nested OpenClaw → Hermes-agent provider test retried missing Hermes
22+
tools hundreds of times from one inbound Telegram message.
23+
1024
## 2026-06-21
1125

1226
### Skill id rename: `perkos-tech``perkos-knowledge` (both runtimes)

images/hermes/config/hermes.template.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,24 @@ mcp_servers:
6969
agent:
7070
id: ${PERKOS_AGENT_ID}
7171
name: ${PERKOS_AGENT_NAME}
72+
# Bound the number of model/tool turns in one inbound request. Hermes
73+
# defaults to 90, which is too permissive for messaging channels: a bad
74+
# provider/tool pairing can otherwise keep a Telegram conversation active
75+
# for close to an hour. Operators can raise this for long-running workers.
76+
max_turns: ${PERKOS_AGENT_MAX_TURNS}
77+
78+
# Messaging surfaces should deliver a conversational answer, not every
79+
# mid-turn planning sentence. Telegram has permanent bubbles, so interim
80+
# assistant commentary and token streaming are disabled while the final answer
81+
# and long-running heartbeat remain available.
82+
display:
83+
platforms:
84+
telegram:
85+
tool_progress: off
86+
interim_assistant_messages: false
87+
busy_ack_detail: false
88+
streaming: false
89+
long_running_notifications: true
7290

7391
# Standalone tool plugins staged into $HERMES_HOME/plugins/ by the entrypoint.
7492
# Hermes gates user/standalone plugins behind this allow-list (unset = none),

images/hermes/docker-entrypoint.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ API_SERVER_ENABLED=$(truthy "${API_SERVER_ENABLED:-true}")
7171
API_SERVER_HOST="${API_SERVER_HOST:-0.0.0.0}"
7272
API_SERVER_PORT="${API_SERVER_PORT:-8642}"
7373

74+
# A channel request must have a finite tool/model budget. Upstream Hermes uses
75+
# 90 turns by default; 30 is ample for interactive work and prevents malformed
76+
# provider/tool combinations from looping for an hour. Keep it configurable
77+
# for autonomous workers that intentionally need a larger budget.
78+
PERKOS_AGENT_MAX_TURNS="${PERKOS_AGENT_MAX_TURNS:-30}"
79+
case "$PERKOS_AGENT_MAX_TURNS" in
80+
''|*[!0-9]*)
81+
echo "perkos-entrypoint: PERKOS_AGENT_MAX_TURNS must be a positive integer" >&2
82+
exit 2
83+
;;
84+
esac
85+
if [ "$PERKOS_AGENT_MAX_TURNS" -lt 1 ]; then
86+
echo "perkos-entrypoint: PERKOS_AGENT_MAX_TURNS must be at least 1" >&2
87+
exit 2
88+
fi
89+
7490
# Messaging gateways default OFF — a wallet enables them via the
7591
# /agents/new wizard which sets *_ENABLED via ecsProvision.
7692
TELEGRAM_ENABLED=$(truthy "${TELEGRAM_ENABLED:-}")
@@ -80,7 +96,7 @@ SLACK_ENABLED=$(truthy "${SLACK_ENABLED:-}")
8096
SLACK_CHANNEL_ID="${SLACK_CHANNEL_ID:-}"
8197
FARCASTER_ENABLED=$(truthy "${FARCASTER_ENABLED:-}")
8298

83-
export API_SERVER_ENABLED API_SERVER_HOST API_SERVER_PORT
99+
export API_SERVER_ENABLED API_SERVER_HOST API_SERVER_PORT PERKOS_AGENT_MAX_TURNS
84100
export TELEGRAM_ENABLED TELEGRAM_ALLOWED_USERS TELEGRAM_HOME_CHANNEL
85101
export SLACK_ENABLED SLACK_CHANNEL_ID
86102
export FARCASTER_ENABLED

tests/hermes/smoke.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ else
235235
fail "config.yaml missing inline 'api_key:' binding (BYOK 401 regression — see hermes.template.yaml model.api_key)"
236236
fi
237237

238+
if echo "$config" | grep -q "max_turns: 30"; then
239+
pass "config.yaml bounds each agent request to 30 turns by default"
240+
else
241+
fail "config.yaml missing the default max_turns safety budget"
242+
fi
243+
244+
if echo "$config" | grep -A8 '^ telegram:' | grep -q "interim_assistant_messages: false"; then
245+
pass "telegram suppresses permanent interim assistant messages"
246+
else
247+
fail "telegram must suppress interim assistant messages to avoid progress spam"
248+
fi
249+
250+
if echo "$config" | grep -A8 '^ telegram:' | grep -q "streaming: false"; then
251+
pass "telegram emits a final response instead of token-stream bubbles"
252+
else
253+
fail "telegram streaming must be disabled for stable conversational delivery"
254+
fi
255+
238256
# --- Open-source skills install (PERKOS_AGENT_SKILLS_B64) ---
239257
# Boot a second container with a skills payload: one allow-listed entry
240258
# (real SHA-pinned ethskills SKILL.md) + one non-allow-listed host. Assert

0 commit comments

Comments
 (0)