Hermes Agent Deep Cuts: A Run Budget Is a Nudge, Not a Kill Switch
A 900-second job can reach its 80% mark, receive a wrap-up instruction, and still run past 900 seconds. Hermes’s --run-budget is not a timer that kills the process. It nudges the model to finish, and narrows some implicit provider timeouts so one stalled request is less likely to consume the remaining window.
That distinction matters when a CI worker, eval harness, or scheduled task has an actual external deadline. If the caller expects Hermes to stop at the second hand, this setting alone does not provide that contract.
The clock changes two parts of the loop
The CLI accepts a floating-point number of seconds with --run-budget. You can set a default under agent.run_budget_seconds; the flag wins for that invocation. The default is null, which leaves the behavior off.
The clock starts for a conversation run, not for the entire Hermes process. Each new user message begins a new run with its own budget. --max-turns is separate: it caps tool-calling iterations, while the run budget measures elapsed wall time.
At 80% elapsed, Hermes tries to append one wrap-up notice to the newest tool result. That placement is deliberate. The next model request sees the notice as part of the tool result it is already processing. Hermes does not insert a synthetic user message, and the notice is only injected once.
There is a cache and persistence constraint here. The code only mutates the current, not-yet-persisted tool-result tail. If there is no eligible tool result, or the newest candidate has already been persisted, injection does not happen at that point. The latch remains open when the append fails, so a later eligible tool result can carry it. This is a coordination hint delivered through the existing loop, not a scheduler interrupt.
Use it with an outer deadline
For a one-shot run, pair a budget with your caller’s own timeout. Leave headroom for the caller to collect output and clean up:
hermes chat --run-budget 850 -q "Inspect the changed files and report verified test results"
For a default that applies to future runs in the profile:
agent:
run_budget_seconds: 850
Hermes also adjusts implicit stale timeouts on provider requests. For a cloud request, the budget caps eligible timeout patience at half the remaining time, with a 60-second floor, and never lengthens a timeout that was already shorter. An explicitly configured provider stale_timeout_seconds or HERMES_API_CALL_STALE_TIMEOUT remains untouched. Streaming requests have a separate stale-timeout path; HERMES_STREAM_STALE_TIMEOUT is explicit there and also takes precedence. Local model prefill grace is not shortened by the run budget.
That precedence is useful when an endpoint needs a known patience window, but it means the budget cannot be treated as a hard ceiling. A tool process with its own timeout, local model prefill grace, explicit provider timeout, or work already in progress is not forcibly terminated by the wrap-up notice. Put a hard timeout around the Hermes process in the parent system when the parent must enforce one.
The gotcha: an unset clock is not a deadline
The budget is fully dormant when unset or non-positive. A YAML boolean such as true is not interpreted as a one-second budget. CLI values are parsed as numbers, but a tiny positive budget is still just a tiny positive budget: the wrap-up message can only reach the model when the loop processes a tool result and makes another model request.
The configuration precedence is straightforward, but the operational trap is treating two cooperating mechanisms as one guarantee. The wrap-up notice asks the model to stop discovery and return a deliverable. The stale-timeout cap limits implicit provider-call patience. Neither is an unconditional process watchdog.
Verify the wiring before trusting it
First confirm the installed CLI exposes the option:
hermes chat --help | rg -A4 -- '--run-budget'
Then inspect the implementation and its focused tests in the source checkout. The tests cover the 80% threshold, one-time injection, the 60-second timeout floor, the fact that a cap never lengthens an already shorter timeout, and the precedence of explicit timeout settings. The test file is tests/agent/test_run_budget.py.
The implementation is split across hermes_cli/_parser.py, where the option is parsed, hermes_cli/cli_init_mixin.py, where the CLI value takes precedence over config, agent/conversation_loop.py, which injects the wrap-up notice, and agent/chat_completion_helpers.py, which caps eligible stale timeouts.
For automation, keep the operating-system or CI deadline outside Hermes. Treat --run-budget as a chance for the agent to stop doing optional work before that deadline, not as proof that it cannot overrun.