My Hermes agent's stop condition was a 40-line if/elif chain. I replaced it with 3 lines. A developer replaced a 40-line if/elif chain for stopping a Hermes research agent with a three-line solution using the new `agent-loop-stop` library. The library provides composable stop conditions like `after_n_turns()`, `cost_exceeds()`, and `response_contains()`, which can be combined with operators or functions. The package, which requires only the Python standard library, allows different agents to use named, reusable stop configurations. This is a submission for the Hermes Agent Challenge. My Hermes research agent's stop logic had grown into a 40-line if/elif block. Stop after 20 turns. Stop if cost exceeds $2. Stop if the response contains "FINAL ANSWER". Stop if the last tool called was "write summary". Each condition was written out longhand, tested independently, and hard to reuse across different agents. I extracted the pattern into agent-loop-stop . python from agent loop stop import any of, after n turns, cost exceeds, response contains stopper = any of after n turns 20 , cost exceeds 2.00 , response contains "FINAL ANSWER" , for turn in range 1, 100 : response = call llm messages state = {"turn": turn, "cost usd": running cost, "response": response.text} if stopper.check state : break That's it. stopper.check state returns True when any condition fires. The state dict can have whatever you want in it — built-in conditions read well-known keys. after n turns 20 state "turn" = 20 cost exceeds 2.00 state "cost usd" 2.00 response contains "FINAL ANSWER" case-insensitive substring last tool was "write summary" state "last tool" == name custom lambda s: s.get "retries" 3 any callable always always True testing never always False placeholder Stop when either fires c = after n turns 20 | cost exceeds 1.00 Stop only when both fire c = after n turns 10 & cost exceeds 0.50 Invert c = ~response contains "continue" Or use the function form: any of after n turns 20 , cost exceeds 2.00 , response contains "done" all of after n turns 5 , cost exceeds 0.25 negate response contains "error" Both styles work identically. The operator form is more concise; the function form is more explicit about what's happening. python from agent loop stop import check all result = check all state, { "turn limit": after n turns 20 , "cost limit": cost exceeds 2.00 , "done signal": response contains "FINAL ANSWER" , }, if result.stopped: log.info f"Agent stopped. Reason s : {result.triggered}" "turn limit" or "done signal" or "cost limit", "done signal" check all checks every named condition individually and returns a StopResult with which ones triggered. This is what I log in my Hermes agent — if I see "turn limit" fired instead of "done signal", that means the agent ran out of turns without finishing. c = custom lambda s: len s.get "tool calls this turn", 5 Or subclass for reusable predicates: python from agent loop stop import StopCondition class TokenBudgetStop StopCondition : def init self, limit: int : self. limit = limit def check self, state : return state.get "tokens used", 0 self. limit stopper = any of after n turns 20 , TokenBudgetStop 4000 Different Hermes agents have different stop requirements: SUPERVISOR STOP = any of after n turns 50 , cost exceeds 5.00 , response contains "SYNTHESIS COMPLETE" , WORKER STOP = any of after n turns 15 , cost exceeds 0.50 , last tool was "submit findings" , Named, reusable, composable. Each agent gets its own stop config that says exactly what it means. Standard library only: dataclasses , typing . No third-party packages. pip install agent-loop-stop