mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
feat(agent): add pre_verify hook and verify-on-stop coding guidance
Add a `pre_verify` user/plugin/shell hook fired once per turn when the agent
edited code and is about to finish, after the existing verify-on-stop guard. A
hook can keep the agent going one more turn (run a check, defer it, tidy the
diff) by returning {"action":"continue","message":...} (the Claude-Code Stop
shape {"decision":"block","reason":...} is accepted too). Hooks receive coding,
attempt, final_response, and sorted changed_paths so they can self-scope and
self-throttle; the path is bounded by agent.max_verify_nudges and preserves
message-role alternation.
Hermes still ships its default coding guidance (agent.verify_guidance, on by
default), but it now rides the evidence-based verify-on-stop missing-evidence
nudge instead of a separate default pre_verify continuation, so it costs no
extra model turn of its own. Guidance reuses the shared utils.is_truthy_value
parser rather than a local copy.
This commit is contained in:
parent
14c4a849b7
commit
a10113658b
14 changed files with 458 additions and 1 deletions
|
|
@ -136,6 +136,17 @@ VALID_HOOKS: Set[str] = {
|
|||
"transform_llm_output",
|
||||
"pre_llm_call",
|
||||
"post_llm_call",
|
||||
# Verification-loop gate. Fired once per turn when the agent has edited code
|
||||
# and is about to verify/finish (after the verify-on-stop guard). A callback
|
||||
# may keep the agent going — run a check, defer it, tidy the diff — instead
|
||||
# of stopping by returning:
|
||||
# {"action": "continue", "message": "<follow-up instruction>"}
|
||||
# The Claude-Code Stop shape {"decision": "block", "reason": "..."} (block
|
||||
# the stop == keep going) is accepted too. Anything else lets the turn
|
||||
# finish. Hermes' shipped guidance lives in the evidence-based
|
||||
# verification-stop nudge; this hook is for user/plugin policy and is
|
||||
# bounded by agent.max_verify_nudges.
|
||||
"pre_verify",
|
||||
"pre_api_request",
|
||||
"post_api_request",
|
||||
"api_request_error",
|
||||
|
|
@ -2029,6 +2040,57 @@ def get_pre_tool_call_block_message(
|
|||
return None
|
||||
|
||||
|
||||
def get_pre_verify_continue_message(
|
||||
*,
|
||||
session_id: str = "",
|
||||
platform: str = "",
|
||||
model: str = "",
|
||||
coding: bool = False,
|
||||
attempt: int = 0,
|
||||
final_response: str = "",
|
||||
changed_paths: Optional[List[str]] = None,
|
||||
) -> Optional[str]:
|
||||
"""Check user ``pre_verify`` hooks for a directive to keep the agent going.
|
||||
|
||||
Fired once per turn when the agent edited code and is about to verify/finish.
|
||||
A hook keeps the turn going (run a check, defer it, tidy the diff) by
|
||||
returning::
|
||||
|
||||
{"action": "continue", "message": "<follow-up for the model>"}
|
||||
|
||||
The Claude-Code Stop shape ``{"decision": "block", "reason": "..."}`` (block
|
||||
the stop == keep going) is accepted too. The first directive carrying a
|
||||
non-empty message wins; any other return lets the turn finish. Mirrors
|
||||
:func:`get_pre_tool_call_block_message` — the call site stays a one-liner.
|
||||
|
||||
``coding`` / ``attempt`` let a hook scope itself (``if not coding`` …) and
|
||||
self-throttle (``if attempt`` …), the same way a ``pre_tool_call`` hook
|
||||
scopes on ``tool_name``.
|
||||
"""
|
||||
hook_results = invoke_hook(
|
||||
"pre_verify",
|
||||
session_id=session_id,
|
||||
platform=platform,
|
||||
model=model,
|
||||
coding=coding,
|
||||
attempt=attempt,
|
||||
final_response=final_response,
|
||||
changed_paths=list(changed_paths or []),
|
||||
)
|
||||
|
||||
for result in hook_results:
|
||||
if not isinstance(result, dict):
|
||||
continue
|
||||
action = str(result.get("action") or result.get("decision") or "").strip().lower()
|
||||
if action not in ("continue", "block"):
|
||||
continue
|
||||
message = result.get("message") or result.get("reason")
|
||||
if isinstance(message, str) and message.strip():
|
||||
return message.strip()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _ensure_plugins_discovered(force: bool = False) -> PluginManager:
|
||||
"""Return the global manager after ensuring plugin discovery has run.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue