hermes-agent-features/hermes_cli
Teknium a989a79c0c
fix(gateway): allow native delivery of freshly-produced agent files (#32060)
The gateway's media delivery allowlist required files live inside
`~/.hermes/cache/{documents,images,...}`, which is the wrong shape for
real agent usage. Agents naturally produce artifacts via terminal tools
(`pandoc -o /tmp/report.pdf`, `matplotlib savefig`, etc.) or
write_file into project directories — these never land under the cache.
Result: users got a raw file path in chat instead of an attachment.

This is doubly bad in deployment shapes where the cache directories
aren't writable by the agent at all: Hermes running in Docker with a
read-only mount, or with a Docker/Modal/SSH terminal backend whose
filesystem isn't the gateway host's filesystem.

Layered trust model:

1. Cache-dir allowlist (unchanged) — Hermes-managed roots always trusted.
2. Operator allowlist — `HERMES_MEDIA_ALLOW_DIRS` env var, now also
   surfaced as `gateway.media_delivery_allow_dirs` in config.yaml.
3. Recency-based trust (new, default on) — files whose mtime is within
   `gateway.trust_recent_files_seconds` (default 600s) of "now" are
   trusted even outside the cache/operator allowlist. Old host files
   (`/etc/passwd`, `~/.bashrc`, `~/.ssh/id_rsa`) have mtimes measured
   in days/months, well outside the window — prompt-injection paths
   pointing at pre-existing files are still rejected.
4. Hard denylist — `/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`,
   `/var/{log,lib,run}`, plus `$HOME/.{ssh,aws,gnupg,kube,docker,config,
   azure,gcloud}` and `Library/Keychains`. Denylist blocks delivery
   even when recency would trust the file, in case an attacker
   somehow refreshes a sensitive file's mtime.

Operators who want strict-allowlist behavior set
`gateway.trust_recent_files: false` and the system reverts to
pre-existing behavior.

Tests: 6 new cases in test_platform_base.py cover the recency window,
disabled mode, system-path denylist, and the motivating PDF-in-project
scenario. 3 existing tests (test_platform_base, test_tts_media_routing,
test_send_message_tool) that exercised the strict-allowlist path are
updated to disable recency trust explicitly.

E2E validation: real `validate_media_delivery_path()` accepts fresh
PDFs in /tmp and project dirs, rejects /etc/passwd, ~/.ssh/id_rsa, and
files older than the window; config.yaml `gateway.*` keys bridge
correctly to the env vars the validator reads.
2026-05-25 05:34:31 -07:00
..
proxy fix(security): validate Nous Portal inference_base_url against host allowlist 2026-05-22 14:17:40 -07:00
__init__.py
_parser.py Fix CLI verbose tool progress config fallback 2026-05-23 21:03:51 -07:00
_subprocess_compat.py
auth_commands.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
auth.py feat(providers): add OpenAI API provider option 2026-05-25 00:59:53 -07:00
azure_detect.py
backup.py fix(backup): skip symlinked files in zip archives (#25289) 2026-05-25 05:07:52 -07:00
banner.py
browser_connect.py feat: auto-launch Chromium-family browser for CDP 2026-05-19 22:34:05 -07:00
bundles.py feat(skills): add skill bundles — alias /<name> loads multiple skills (#28373) 2026-05-18 21:38:05 -07:00
callbacks.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
checkpoints.py
claw.py
cli_output.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
clipboard.py
codex_models.py
codex_runtime_plugin_migration.py
codex_runtime_switch.py
colors.py
commands.py feat(skills): add opt-in AST deep diagnostics 2026-05-23 17:47:26 -07:00
completion.py
config.py fix(gateway): allow native delivery of freshly-produced agent files (#32060) 2026-05-25 05:34:31 -07:00
container_boot.py fix(docker): make s6 lifecycle work for the unprivileged hermes user 2026-05-25 12:23:23 +10:00
copilot_auth.py
cron.py
curator.py
curses_ui.py fix(cli): clamp curses color 8 for 8-color terminals (Docker) 2026-05-21 23:40:58 -07:00
debug.py fix(debug): redact BlueBubbles webhook secrets 2026-05-24 15:43:48 -07:00
default_soul.py
dep_ensure.py
dingtalk_auth.py
doctor.py fix(security): tighten .env file permissions to 0600 at all creation sites 2026-05-25 03:40:47 -07:00
dump.py fix(skills): prune dependency/venv dirs from all skill scanners (#30042) 2026-05-21 14:18:02 -07:00
env_loader.py perf(cli): cut hermes startup 63% — flip head-to-head vs codex (#31968) 2026-05-25 03:06:39 -07:00
fallback_cmd.py fix(fallback): merge fallback_providers with legacy fallback_model configurations 2026-05-23 05:24:57 -07:00
fallback_config.py fix(fallback): merge fallback_providers with legacy fallback_model configurations 2026-05-23 05:24:57 -07:00
gateway_windows.py fix(gateway-windows): atomic write for .cmd and startup launcher scripts 2026-05-23 02:30:41 -07:00
gateway.py Merge pull request #31760 from NousResearch/hermes/hermes-bf5898da 2026-05-25 12:57:51 +10:00
goals.py
hooks.py
inventory.py
kanban_db.py fix(kanban): scratch tasks must not inherit board.default_workdir (#28818) 2026-05-24 15:48:58 -07:00
kanban_decompose.py
kanban_diagnostics.py
kanban_specify.py
kanban_swarm.py feat(cli): add kanban swarm topology helper 2026-05-18 21:10:12 -07:00
kanban.py feat(kanban): --ids bulk promote + AUTHOR_MAP entry for #29464 2026-05-23 23:10:36 -07:00
logs.py
main.py fix(update): reject symlink members in update ZIP 2026-05-25 05:15:55 -07:00
mcp_config.py
memory_setup.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
migrate.py feat(cli): hermes migrate xai [--apply] [--no-backup] 2026-05-20 09:18:23 -07:00
model_catalog.py
model_normalize.py
model_switch.py fix(model-switch): mark bare custom provider as current 2026-05-19 10:57:35 -07:00
models.py feat(providers): extend openai-api with live /v1/models fetch + gpt-5.5-pro 2026-05-25 00:59:53 -07:00
nous_subscription.py
oneshot.py fix(provider): make config.yaml model.provider the single source of truth (#31222) 2026-05-23 18:18:41 -07:00
pairing.py
platforms.py
plugins_cmd.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
plugins.py feat(stt): add register_transcription_provider() plugin hook 2026-05-25 01:41:19 -07:00
portal_cli.py feat(portal): one-shot setup, status CLI, and Nous-included markers (#30860) 2026-05-23 02:39:09 -07:00
profile_describer.py fix(skills): prune dependency/venv dirs from all skill scanners (#30042) 2026-05-21 14:18:02 -07:00
profile_distribution.py fix(profile): reject symlinks in distributions (#25292) 2026-05-25 05:07:58 -07:00
profiles.py fix(security): tighten .env file permissions to 0600 at all creation sites 2026-05-25 03:40:47 -07:00
providers.py feat(providers): add OpenAI API provider option 2026-05-25 00:59:53 -07:00
pt_input_extras.py
pty_bridge.py
relaunch.py
runtime_provider.py fix(custom): pass custom provider extra body 2026-05-21 07:48:53 -07:00
secret_prompt.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
secrets_cli.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
security_advisories.py
security_audit.py feat(security): on-demand supply-chain audit via OSV.dev (#31460) 2026-05-24 15:15:16 -07:00
send_cmd.py
service_manager.py fix(docker): make s6 lifecycle work for the unprivileged hermes user 2026-05-25 12:23:23 +10:00
session_recap.py
setup.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
skills_config.py
skills_hub.py fix: harden skill trust source matching (#31229) 2026-05-25 01:51:15 -07:00
skin_engine.py
slack_cli.py
status.py refactor(ntfy): convert built-in adapter to platform plugin 2026-05-23 16:13:01 -07:00
stdio.py
timeouts.py perf(agent-loop): cut 47% of per-conversation function calls via 3 targeted hot-path optimizations (#28866) 2026-05-19 14:25:10 -07:00
tips.py feat: auto-launch Chromium-family browser for CDP 2026-05-19 22:34:05 -07:00
tools_config.py fix(model): include Premium+ in xAI OAuth label 2026-05-24 18:12:16 -07:00
uninstall.py
vercel_auth.py
voice.py
web_server.py fix(cron): reject id mutation + validate output paths under OUTPUT_DIR 2026-05-25 01:15:24 -07:00
webhook.py fix(state): restrict sensitive store file permissions 2026-05-24 04:55:18 -07:00
xai_retirement.py fix(xai): align migrate retirement map with docs 2026-05-20 09:18:23 -07:00