Repository navigation
Conversation
|
Nice to see this on real code so fast — and the core wiring is in the right place: gating in finalize_turn right after the transform_llm_output block, with a block_final failure replacing final_response and setting failed=True / completed=False, is the correct chokepoint (every surface reads final_response from there) and it fails closed. Good foundation. A few things before this leaves draft, the first one load-bearing:
spec.command is before_final.command lifted verbatim from SKILL.md frontmatter, with no allowlist, no sandbox, and no user confirmation. The timeout bounds how long it runs, not what it can do. And because the spec is recorded on a successful main skill_view (point 2), merely viewing a skill causes its declared command to be shell-run at the next turn boundary. A command:-carrying skill can arrive two ways neither of which involves a human vetting that
So, if the verification machinery itself throws, the unverified response proceeds. For a feature whose whole point is failing closed, an infrastructure error should arguably block (or be explicitly configurable), not silently pass — otherwise an exception becomes a verification bypass. Minor: tests/gateway/test_telegram_topic_mode.py (+4) looks unrelated to this change (it's a get_random_tip determinism patch) and probably shouldn't ride in this diff. Happy to help on the runner side once the activation + trust-boundary direction is settled — the command-execution surface is the part I'd most want a second pair of eyes on. |
06e5e8a to
ef58c00
Compare
|
Pushed
Local checks run:
CI is rerunning on the new head now. |
Address review feedback by separating skill load from activation, disabling command verifiers by default, avoiding shell execution, and failing closed when verification infrastructure errors.
ef58c00 to
116d595
Compare
|
Rebased this onto current |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing the initial load/activation and fail-closed feedback. The PR still has blocking issues before it can provide an operable, safe gate.
Problems
agent/skill_verification.py:373callscfg_getwith a dotted string instead of a config dict and key path.cfg_getreturns its default for non-dict input (hermes_cli/config.py:6611-6654), soskills.verification.allow_commands: truecan never enable a verifier. The tests monkeypatch this function attests/agent/test_skill_verification.py:58and:130, so they miss the real path.agent/skill_verification.py:232executes argv from SKILL.md frontmatter.shell=Falseremoves shell parsing, but does not authorize arbitrary executables. This remains a trust-boundary issue for mutable external skills, which are fully integrated by design (website/docs/user-guide/features/skills.md:319-336).- The new global verifier maps (
agent/skill_verification.py:25-26) are never cleared by a production lifecycle caller. Existing shutdown/rotation seams arerun_agent.py:3315-3365; the PR addsclear_session_verifications()but does not invoke it.
Suggested changes
- Fix the config lookup and add a temp-
HERMES_HOMEintegration test without monkeypatching it. - Define a trusted runner/provenance or approval boundary for command-bearing skills.
- Clear state at session close and rotation, with lifecycle coverage.
Automated hermes-sweeper review.
| try: | ||
| from hermes_cli.config import cfg_get | ||
|
|
||
| return bool(cfg_get(_ALLOW_COMMANDS_CONFIG_KEY, False)) |
There was a problem hiding this comment.
cfg_get takes a config dict followed by individual path keys (cfg_get(cfg, "skills", "verification", "allow_commands", default=False)). Passing the dotted string as cfg makes it return None, so this gate is permanently false. Please use the loaded config and add an unmocked config.yaml integration test.
| error="empty verifier command", | ||
| ) | ||
| try: | ||
| completed = subprocess.run( |
There was a problem hiding this comment.
shell=False prevents shell interpretation but does not make this frontmatter-controlled executable safe. Once the global switch is enabled, a mutable externally installed or agent-authored SKILL.md can select any executable and arguments. This needs a runtime trust/provenance, vetted-runner, or approval boundary before command execution is enabled.
| return list(_PENDING_BY_SESSION.get(key, {}).values()) | ||
|
|
||
|
|
||
| def clear_session_verifications(session_id: str | None) -> None: |
There was a problem hiding this comment.
This cleanup helper is not wired into any production session close or rotation path, while the module retains per-session global maps above. Please invoke it from the existing AIAgent lifecycle seams and add closed-session and rotated-session coverage.
Summary
metadata.hermes.verification.before_finalskill_viewloads the mainSKILL.mdfinalize_turnbefore the final response and block failedblock_finalresponsesDraft for #44637. This MVP treats a successful main
skill_viewcall as activation; explicit activation and streaming suppression are intentionally left for maintainer direction.Tests
scripts/run_tests.sh tests/agent/test_skill_verification.py -- -qscripts/run_tests.sh tests/agent/test_skill_commands.py tests/agent/test_external_skills.py -- -q.venv/bin/ruff check agent/skill_verification.py agent/turn_finalizer.py tools/skills_tool.py tests/agent/test_skill_verification.py