Bug description
Hermes Desktop/TUI on a hosted Hermes 0.20.0 runtime cannot represent a durable, explicit zero-model-tool state through supported configuration.
This is security-sensitive because an administrator can persist and read back empty tool configuration while a genuinely fresh Desktop session still exposes the default tool catalog.
Reproduction
Persist configuration equivalent to:
toolsets: []
platform_toolsets:
desktop: []
cli: []
tools:
tool_search:
enabled: false
Also disable all selectable skills and MCP servers, then construct a genuinely fresh Desktop/TUI session and inspect the live agent through session-aware tools.show(session_id) plus session.info.tools.
Observed on Hermes 0.20.0:
Persisted/effective toolsets: []
Enabled selectable skills: 0
MCP servers: 0
Fresh live-agent tools: 47
No model prompt is required to reproduce the exposure.
Source-level cause
tui_gateway.server._load_enabled_toolsets() currently follows this pattern after loading configured CLI toolsets:
enabled = _get_platform_tools(cfg, "cli", include_default_mcp_servers=True)
if not enabled:
return None
return sorted(enabled | _gui_surface_toolsets(session_platform))
An explicit empty configuration is therefore converted to None, which downstream means default/unrestricted selection. A non-empty minimal configuration is not an equivalent workaround because _gui_surface_toolsets() injects project, and for Desktop also desktop_ui.
agent.disabled_toolsets is also not currently forwarded by the Desktop/TUI gateway. PR #44505 addresses that separate propagation gap but remains open.
Process override is not a durable hosted workaround
HERMES_TUI_TOOLSETS=context_engine can produce zero schemas because context_engine is a valid non-empty toolset that currently resolves to no model tools and returns before GUI-surface injection.
On a hosted runtime, however, persisting that value through the dashboard environment API and invoking reload.env produced zero tools only on the worker handling that RPC. A later independent WebSocket/session exposed 47 tools. A full instance Stop → Start preserved the on-disk value but the first independent post-restart session still exposed 47 tools.
This shows that a worker-local process override is not a durable replacement for an explicit configuration state in a multi-worker hosted deployment.
Expected behavior
Hermes should provide one supported, restart-durable configuration representation for zero model-callable tools, with fail-closed semantics across Desktop/TUI workers.
Acceptable designs include:
- preserve explicit empty selection as
[] rather than converting it to None;
- add an explicit
agent.disable_all_tools: true setting; or
- make
agent.disabled_toolsets effective across every Desktop/TUI agent-construction path and capable of suppressing automatic GUI surfaces.
Automatic project/desktop_ui injection must not override an explicit zero-tool state.
Acceptance criteria
- Persisted explicit zero state survives process and instance restart.
- Three independently constructed Desktop/TUI WebSocket sessions each report:
tools.show.total == 0
tools.show.sections == []
- zero names in
session.info.tools
- Zero state remains zero with no skills and no MCP servers.
- Missing configuration retains current defaults for backward compatibility.
- Invalid toolset names fail closed or produce a blocking configuration error; they must not silently enable defaults.
- Add request-level regression coverage for main session creation,
tools.show, background/preview agents, and MCP reload.
Environment
- Hermes Agent: 0.20.0
- Surface: Hermes Desktop connected to a hosted dashboard/TUI gateway
- Hosted instance identity and credentials intentionally omitted
Related
Bug description
Hermes Desktop/TUI on a hosted Hermes 0.20.0 runtime cannot represent a durable, explicit zero-model-tool state through supported configuration.
This is security-sensitive because an administrator can persist and read back empty tool configuration while a genuinely fresh Desktop session still exposes the default tool catalog.
Reproduction
Persist configuration equivalent to:
Also disable all selectable skills and MCP servers, then construct a genuinely fresh Desktop/TUI session and inspect the live agent through session-aware
tools.show(session_id)plussession.info.tools.Observed on Hermes 0.20.0:
No model prompt is required to reproduce the exposure.
Source-level cause
tui_gateway.server._load_enabled_toolsets()currently follows this pattern after loading configured CLI toolsets:An explicit empty configuration is therefore converted to
None, which downstream means default/unrestricted selection. A non-empty minimal configuration is not an equivalent workaround because_gui_surface_toolsets()injectsproject, and for Desktop alsodesktop_ui.agent.disabled_toolsetsis also not currently forwarded by the Desktop/TUI gateway. PR #44505 addresses that separate propagation gap but remains open.Process override is not a durable hosted workaround
HERMES_TUI_TOOLSETS=context_enginecan produce zero schemas becausecontext_engineis a valid non-empty toolset that currently resolves to no model tools and returns before GUI-surface injection.On a hosted runtime, however, persisting that value through the dashboard environment API and invoking
reload.envproduced zero tools only on the worker handling that RPC. A later independent WebSocket/session exposed 47 tools. A full instance Stop → Start preserved the on-disk value but the first independent post-restart session still exposed 47 tools.This shows that a worker-local process override is not a durable replacement for an explicit configuration state in a multi-worker hosted deployment.
Expected behavior
Hermes should provide one supported, restart-durable configuration representation for zero model-callable tools, with fail-closed semantics across Desktop/TUI workers.
Acceptable designs include:
[]rather than converting it toNone;agent.disable_all_tools: truesetting; oragent.disabled_toolsetseffective across every Desktop/TUI agent-construction path and capable of suppressing automatic GUI surfaces.Automatic
project/desktop_uiinjection must not override an explicit zero-tool state.Acceptance criteria
tools.show.total == 0tools.show.sections == []session.info.toolstools.show, background/preview agents, and MCP reload.Environment
Related