Restore local STT command fallback for voice transcription, detect whisper and ffmpeg in common local install paths, and avoid bogus no-provider messaging when only a backend-specific key is missing.
- Add 'emoji' field to ToolEntry and 'get_emoji()' to ToolRegistry
- Add emoji= to all 50+ registry.register() calls across tool files
- Add get_tool_emoji() helper in agent/display.py with 3-tier resolution:
skin override → registry default → hardcoded fallback
- Replace hardcoded emoji maps in run_agent.py, delegate_tool.py, and
gateway/run.py with centralized get_tool_emoji() calls
- Add 'tool_emojis' field to SkinConfig so skins can override per-tool
emojis (e.g. ares skin could use swords instead of wrenches)
- Add 11 tests (5 registry emoji, 6 display/skin integration)
- Update AGENTS.md skin docs table
Based on the approach from PR #1061 by ForgingAlex (emoji centralization
in registry). This salvage fixes several issues from the original:
- Does NOT split the cronjob tool (which would crash on missing schemas)
- Does NOT change image_generate toolset/requires_env/is_async
- Does NOT delete existing tests
- Completes the centralization (gateway/run.py was missed)
- Hooks into the skin system for full customizability
SSH persistent shell now defaults to true — non-local backends benefit
most from state persistence across execute() calls. Local backend
remains opt-in via TERMINAL_LOCAL_PERSISTENT env var.
New config.yaml option: terminal.persistent_shell (default: true)
Controls the default for non-local backends. Users can disable with:
hermes config set terminal.persistent_shell false
Precedence: per-backend env var > TERMINAL_PERSISTENT_SHELL > default.
Wired through cli.py, gateway/run.py, and hermes_cli/config.py so the
config.yaml value reaches terminal_tool via env var bridge.
When a cronjob is created from within a Telegram or Slack thread,
deliver=origin was posting to the parent channel instead of the thread.
Root cause: the gateway never set HERMES_SESSION_THREAD_ID in the
session environment, so cronjob_tools.py could not capture thread_id
into the job's origin metadata — even though the scheduler already
reads origin.get('thread_id').
Fix:
- gateway/run.py: set HERMES_SESSION_THREAD_ID when thread_id is
present on the session context, and clear it in _clear_session_env
- tools/cronjob_tools.py: read HERMES_SESSION_THREAD_ID into origin
Closes#1219
Extend subprocess env sanitization beyond provider credentials by blocking Hermes-managed tool, messaging, and related gateway runtime vars. Reuse a shared sanitizer in LocalEnvironment and ProcessRegistry so background and PTY processes honor the same blocklist and _HERMES_FORCE_ escape hatch. Add regression coverage for local env execution and process_registry spawning.
Expanded the list of blocked environment variables to include Google, Groq, Mistral, and other major LLM providers. This ensures complete isolation and prevents conflicts with external CLI tools.
Salvaged from PR #1292 onto current main. Preserve per-job model,
provider, and base_url overrides in cron execution, persist them in
job records, expose them through the cronjob tool create/update paths,
and add regression coverage. Deliberately does not persist per-job
api_key values.
- add stt.enabled to the default user config
- make transcription_tools respect the disabled flag globally
- surface disabled state cleanly in voice mode diagnostics
- add regression coverage for disabled STT provider selection
pattern_key was derived by splitting the regex on \b and taking [1],
so patterns starting with the same word (e.g. find -exec rm and
find -delete) produced the same key "find". Approving one silently
approved the other. Using the unique description string as the key
eliminates all collisions.
The fork bomb regex used `()` (empty capture group) and unescaped `{}`
instead of literal `\(\)` and `\{\}`. This meant the classic fork bomb
`:(){ :|:& };:` was never detected. Also added `\s*` between `:` and
`&` and between `;` and trailing `:` to catch whitespace variants.
The execute_code sandbox spawns a child process with cwd set to a
temporary directory, but never adds the hermes-agent project root to
PYTHONPATH. This makes project-root modules like minisweagent_path
unreachable from sandboxed scripts, causing ImportError when the
agent runs self-diagnostic or analysis code via execute_code.
Fix by prepending the hermes-agent root directory to PYTHONPATH in
the child process environment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add base_url/api_key overrides for auxiliary tasks and delegation so users can
route those flows straight to a custom OpenAI-compatible endpoint without
having to rely on provider=main or named custom providers.
Also clear gateway session env vars in test isolation so the full suite stays
deterministic when run from a messaging-backed agent session.
Allow cron runs to keep using send_message for additional destinations, but
skip same-target sends when the scheduler will already auto-deliver the final
response there. Add prompt/tool guidance, docs, and regression coverage for
origin/home-channel resolution and thread-aware comparisons.
- Merge _init_persistent_shell + _start_persistent_shell into single method
- Move execute() dispatcher and cleanup() into PersistentShellMixin
so LocalEnvironment and SSHEnvironment inherit them
- Remove broad except Exception wrappers from _execute_oneshot in both backends
- Replace try/except with os.path.exists checks in local _read_temp_files
and _cleanup_temp_files
- Remove redundant bash -c from SSH oneshot (SSH already runs in a shell)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When vision_analyze_tool fails, the except block was returning a
generic 'could not be analyzed' message that gave the agent no
actionable information about the failure cause.
Replace the generic message with the actual exception string so the
agent can distinguish between backend errors, missing dependencies,
network failures, and unsupported image paths.
Also add an 'error' field to the failure response for structured
error handling by callers.
Fixes#1034
Remove diary-style memory framing from the system prompt and memory tool
schema, explicitly steer task/session logs to session_search, and clarify
that session_search is for cross-session recall after checking the current
conversation first. Add regression tests for the updated guidance text.
Resolve the cherry-pick against current browser_tool structure without carrying unrelated formatting churn, while preserving the intended cleanup, PATH, and screenshot recovery changes from PR #1001.
Unify browser session teardown so manual close, inactivity cleanup, and emergency shutdown all follow the same cleanup path instead of partially duplicating logic.
This changes browser_close() to delegate to cleanup_browser(), which means recording shutdown, Browserbase release, activity bookkeeping cleanup, and local socket-directory removal now happen consistently. It also updates emergency cleanup to route through cleanup_all_browsers() and explicitly clear in-memory tracking state after teardown so stale active-session, last-activity, and recording entries are not left behind on exit.
The screenshot fallback path has also been fixed. _extract_screenshot_path_from_text() now matches real absolute PNG paths, including quoted output, so browser_vision() can recover screenshots when agent-browser emits human-readable text instead of JSON.
Regression coverage was added in tests/tools/test_browser_cleanup.py for screenshot path extraction, cleanup_browser() state removal, browser_close() delegation, and emergency cleanup state clearing.
Verified with:
- python -m pytest tests/tools/test_browser_cleanup.py -q
- python -m pytest tests/tools/test_browser_console.py tests/gateway/test_send_image_file.py -q
Salvaged from PR #1007 by stablegenius49.
- let INSTALL_POLICY decide dangerous verdict handling for builtin skills
- allow --force to override blocked dangerous decisions for trusted and community sources
- accept --yes / -y as aliases for --force in /skills install
- update regression tests to match the intended policy precedence