- Add TestAIAgentConcurrentExecution with 8 integration tests exercising
_execute_tool_calls_concurrent through AIAgent for 2/3/4-tool batches,
pass-rate reporting, and Gemma 4-style read patterns.
- Fix test_malformed_json_args_forces_sequential: use JSON array '[1,2,3]'
instead of unrepairable garbage now that repair_and_load_json handles
most malformed input.
- Fix test_concurrent_handles_tool_error: replace racy call_count list
with deterministic failure based on tool_call_id to eliminate flaky
failures under ThreadPoolExecutor.
Closes#798
_get_vision_model() now resolves via a layered priority chain:
1. BROWSER_VISION_MODEL env var (browser-specific override)
2. config.yaml browser.vision_model
3. AUXILIARY_VISION_MODEL env var (backward-compat shared override)
4. google/gemma-4-27b-it — Gemma 4 native multimodal default
Add browser.vision_model config key to hermes_cli/config.py defaults
with inline documentation.
call_kwargs["model"] is now always set (model is never None), and a
debug log line records which model is in use for each screenshot.
Fixes#816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Building on the Gemma 4 default already on this branch:
- Change call_llm() task from "vision" to "browser_vision" in browser_vision()
so auxiliary.browser_vision.* config is consulted for provider/model/timeout
- Route call_llm(task="browser_vision") through the vision provider resolution
path in auxiliary_client.py (same as task="vision")
- Fix timeout resolution: check auxiliary.browser_vision.timeout before
auxiliary.vision.timeout (allows browser-specific timeout override)
- Add timeout option to auxiliary.browser_vision in cli-config.yaml.example
- Add test_browser_vision_gemma4.py covering: task routing assertions,
call_llm() vision branch routing, and timeout config key ordering
Refs #816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge remote claude/issue-816 which contains the full Gemma 4 browser
vision implementation. Resolved conflicts by taking the remote's cleaner
variable names and docstrings while keeping the same 4-tier resolution
logic. All 12 tests pass.
Refs #816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends `_get_vision_model()` with a 5-level resolution chain:
1. `BROWSER_VISION_MODEL` env var — browser-specific override
2. `auxiliary.browser.vision_model` config key — per-install default
3. `AUXILIARY_VISION_MODEL` env var — backward-compat shared override
4. Auto-select `gemma-4-27b-it` when the main provider is Gemini/Google
5. `None` — fall through to `call_llm` vision router
Adds `_BROWSER_VISION_DEFAULT_MODEL = "gemma-4-27b-it"` constant and
registers `gemma-4-27b-it` in the Gemini provider model catalog.
16 new tests in `tests/tools/test_browser_vision_model.py` cover each
priority level, edge cases (empty env, config exceptions, wrong provider).
Fixes#816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements mTLS for securing agent-to-agent communication in the Hermes
fleet. Fixes#806.
Changes:
- scripts/gen_fleet_ca.sh: generate a self-signed Fleet CA (4096-bit RSA,
10-year validity) that signs all agent certificates
- scripts/gen_agent_cert.sh: generate per-agent certs (Timmy, Allegro,
Ezra) signed by the fleet CA with SAN entries and clientAuth/serverAuth
extended key usage
- agent/mtls.py: new module providing:
- build_server_ssl_context() — TLS_SERVER context with CERT_REQUIRED,
enforces client cert against Fleet CA
- build_client_ssl_context() — TLS_CLIENT context for outbound A2A calls
- MTLSMiddleware — ASGI middleware that rejects unauthenticated requests
to A2A routes (/.well-known/agent-card*, /api/agent-card, /a2a/) with
HTTP 403 when mTLS is enabled
- is_mtls_configured() — checks HERMES_MTLS_CERT/KEY/CA env vars
- hermes_cli/web_server.py: wire MTLSMiddleware into the FastAPI app;
pass SSL context to uvicorn when HERMES_MTLS_* env vars are set so
the server runs TLS with mandatory client cert verification
- ansible/roles/hermes_mtls/: Ansible role to distribute Fleet CA cert,
agent cert, and agent key to fleet nodes; writes an env file with
HERMES_MTLS_* vars and restarts the hermes-gateway service
- ansible/fleet_mtls.yml: fleet-wide playbook referencing the role for
Timmy, Allegro, and Ezra nodes
- tests/test_mtls.py: 15 tests covering is_mtls_configured, SSL context
creation with real cryptography-generated certs, and MTLSMiddleware
(unauthorized agent rejected → 403, authorized agent accepted → 200)
mTLS is opt-in: set HERMES_MTLS_CERT, HERMES_MTLS_KEY, and HERMES_MTLS_CA
to enable. When unset, the server behaves exactly as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Default browser_vision screenshots to google/gemma-4-27b-it (Gemma 4
native multimodal) for reduced latency and unified text+vision model.
Resolution order for _get_vision_model():
1. BROWSER_VISION_MODEL env var (new, browser-specific override)
2. auxiliary.browser_vision.model in config.yaml (new config key)
3. AUXILIARY_VISION_MODEL env var (existing global vision override)
4. Default: google/gemma-4-27b-it
Backward compatibility: existing AUXILIARY_VISION_MODEL users are
unaffected — their override still flows through to browser_vision.
Also documents the new auxiliary.browser_vision config section in
cli-config.yaml.example and adds 14 unit tests covering the full
priority chain.
Fixes#816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_get_vision_model() now always returns a non-empty string (Gemma 4 default
or configured override), so the `if vision_model:` conditional guard is
unnecessary. Replace with unconditional assignment and add a debug log
line showing which model was selected.
Refs #816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Default browser screenshot analysis now uses Gemma 4 27B
(google/gemma-4-27b-it) instead of deferring to the auxiliary router's
auto-detection. Gemma 4 is natively multimodal — the same model family
already in use for text tasks — which avoids cold-start model-switching
overhead and improves context continuity.
Resolution order for _get_vision_model():
1. BROWSER_VISION_MODEL env var (browser-specific override)
2. auxiliary.browser_vision.model in config.yaml
3. AUXILIARY_VISION_MODEL env var (shared/legacy override)
4. google/gemma-4-27b-it (new default)
- Add _BROWSER_VISION_DEFAULT_MODEL constant to browser_tool.py
- Document auxiliary.browser_vision config key in cli-config.yaml.example
- Add 10 unit tests covering all resolution steps
Fixes#816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement a transactional write-validate-commit-or-rollback pattern for
all skill_manage write operations (edit, patch, write_file):
- _backup_skill_file: timestamped .bak.{ts} snapshot before every write
- _validate_written_file: re-reads from disk after write to catch truncation,
encoding errors, and broken YAML frontmatter
- _revert_from_backup: restores original content (or removes the corrupted
file) on any validation failure
- _cleanup_old_backups: prunes to MAX_BACKUPS_PER_FILE (3) after success;
failed edits keep their .bak file as a debugging aid
Also fixes pre-existing issue where _patch_skill error returns lacked a
`suggestion` field expected by test_skill_manager_error_context.py tests.
Adds 21 tests in test_skill_manager_autorevert.py covering every component
and an end-to-end simulation of mid-write failure + auto-revert.
Fixes#923
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>