feat(hermes_governance): opt-in skill-budget governance plugin#374
Open
rinadelph wants to merge 4 commits into
Open
feat(hermes_governance): opt-in skill-budget governance plugin#374rinadelph wants to merge 4 commits into
rinadelph wants to merge 4 commits into
Conversation
53b3eeb to
7d843fa
Compare
Plugin porting Hermes' governance loop into Code Puppy via callback hooks. No core edits, no standalone slash command — controlled entirely through puppy.cfg keys (auto-exposed in /set tab-completion): /set hermes_governance_enabled=true /set hermes_governance_onboarding_budget=5 /set hermes_governance_max_budget=90 - pre/post_tool_call enforcer gates tool use against a skill budget and emits nudges to push the agent toward creating/using skills. - State rides in the conversation via a carrier processor (wrap_pydantic_agent), reset per run on agent_run_start. - session_end runs Hermes-style background curation that archives stale agent-created skills. - Registers a skill_manage tool for on-demand skill lifecycle ops. Every hook fails gracefully and is a no-op while enforcement is disarmed.
7d843fa to
c2f01d7
Compare
Outgoing wire body's tools array was cp_-prefixed but historical tool_use blocks in messages[*].content[*] were not, since pydantic_patches strips the prefix from call.tool_name in-place and that mutation persists into _message_history. The mismatch can wedge follow-up turns once history accumulates. Now we prefix both the live tools catalog and every tool_use block in the messages array. tool_result blocks reference by tool_use_id (not name) so they're left untouched. Adds three regression tests covering: history-only prefixing, mixed catalog+history, and idempotence when already prefixed.
Appending the carrier as a standalone ModelRequest produced two consecutive user messages on the wire (the real prompt + the <<<HERMES_GOVERNANCE_STATE>>> blob). Claude Code OAuth's endpoint silently stalls on consecutive user turns instead of erroring, which hung the agent on the very first call when both plugins were armed. write_state now merges the carrier as an extra UserPromptPart on the last existing ModelRequest, falling back to a standalone message only when no user turn exists yet. _strip_carriers already supported this inline layout, so the original design accommodates it — we just weren't taking advantage on write. Adds six regression tests pinning the new layout contract: merge-into-last, mid-conversation merge, fallback-when-empty, no-duplicate-on-rewrite, find_state-reads-inline-carrier, and strip-preserves-real-content.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in
hermes_governanceplugin that ports a "Hermes-style" governance loop into Code Puppy: a skill-budget gate on tool calls, nudges that steer the agent toward creating/reusing skills, and background curation of stale agent-created skills. Implemented entirely through callback hooks — no core files are modified.What's included
code_puppy/plugins/hermes_governance/:enforcer.py—pre_tool_call/post_tool_callgate that enforces a skill budget and emits nudges.budget.py— the budget primitive (onboarding budget → expanded budget after first skill use).carrier.py/carrier_processor.py— state rides in the conversation viawrap_pydantic_agent; reset per run onagent_run_start.curator.py—session_endbackground curation that archives stale agent-created skills.nudges.py— system-reminder injection onuser_prompt_submit.skill_manage.py— registers askill_managetool for on-demand skill lifecycle ops.config.py— configuration surface (see below).Control surface
No standalone slash command. Governance is controlled entirely through
puppy.cfgkeys (auto-exposed in/settab-completion):Design
pre_tool_call,post_tool_call,wrap_pydantic_agent,agent_run_start,session_end,user_prompt_submit,register_tools) is part of the documented callback surface.Testing
ruff check/ruff formatclean.Risk
Low when disabled (default). When enabled, it intentionally gates tool calls — that's the feature. Happy to adjust scope/behaviour based on review.