hermes-agent/tools
Teknium 243a01d5d7 fix(curator): make the autonomous write policy consistent (#67140)
The background write guard decided ownership from `isinstance(usage_rec, dict)`,
so a local skill with NO usage record passed. That successful write called
bump_patch(), which created a `created_by: null` record — and the identical
write was refused from then on. "Allowed exactly once, then never" is a race
with our own bookkeeping, not a policy. Reproduced on main: patch #1 succeeds,
patch #2 with the same arguments is refused.

Option B from the issue. Option A (split `session_review` from
`scheduled_curator` and let the session fork patch user-owned skills it
consulted) would widen autonomous write permission onto skills the user owns
with no user present to consent — wrong direction for a no-user-present actor.

- skill_manager_tool: missing and explicit-null records now resolve
  IDENTICALLY, both fail closed. The refusal names the reason and points at
  `hermes curator adopt <name>`.
- background_review: both review prompts told the reviewer to patch any skill
  consulted in the session and claimed pinned skills could be improved, while
  enforcement refused both. Prompts now list pinned, external, and user-owned
  skills as protected, and tell the reviewer to RECOMMEND adoption instead of
  attempting a write that will be refused.
- skill_usage: document that `created_by` is a curator-management policy flag,
  not a provenance claim, and add `is_curator_managed()` so call sites read as
  the question they ask. Field name retained — it is on disk in every
  `.usage.json` and renaming would strand those records.
- curator CLI: `hermes curator list-unmanaged` itemizes unmanaged skills with
  the reason each is unmanaged (completes the #67139 spec).

Foreground writes are untouched: a user-directed edit to a user-owned skill
still works, including on pinned skills.

Sibling tests: 9 failures in test_skill_manager_tool.py were fixtures that
created record-less skills to exercise OTHER guards (consolidation-delete,
read-before-write) and relied on ownership falling through. Fixed at the
fixture, since the real curator only ever operates on managed sediment. One
test asserted the old "manually authored" wording; rewritten to assert the
behavior contract instead of the string.

Validation: 274 targeted tests + all 7 background-review files (60 tests) pass.
E2E on a temp HERMES_HOME (30 checks) covers the flip, foreground writes,
adoption unblocking, pin semantics, prompt/enforcement parity, and the new verb.
Each new test sabotage-verified: revert the fix, confirm it goes red.

Fixes #67140
2026-07-25 19:27:17 -07:00
..
computer_use fix: repair sweep fallout — duplicate encoding kwargs, non-subprocess call sites, kwarg-snapshot tests 2026-07-24 11:45:57 -07:00
environments fix: brace-group the filtered export dump so $BASHPID expands in the parent shell 2026-07-24 23:09:02 -07:00
neutts_samples
__init__.py
ansi_strip.py fix(cli): sanitize terminal escapes when replaying stored history (/resume recap, /status recap) 2026-07-17 04:53:38 -07:00
approval.py fix(approval): restore session approval for Tirith-flagged commands 2026-07-21 12:04:47 -07:00
async_delegation.py fix(gateway,tools,agent): close leaked SQLite connections in delivery, delegation, and verification ledgers 2026-07-24 15:55:08 -07:00
binary_extensions.py
blueprints.py fix: widen UTF-8 BOM tolerance to all sibling frontmatter parsers 2026-07-17 04:52:02 -07:00
browser_camofox.py fix(browser): block Camofox input on private pages 2026-07-03 03:27:47 -07:00
browser_camofox_state.py
browser_cdp_tool.py fix(browser): apply private-page guard to browser_cdp frame_id routing 2026-07-03 03:27:47 -07:00
browser_dialog_tool.py feat: auto-launch Chromium-family browser for CDP 2026-05-19 22:34:05 -07:00
browser_supervisor.py fix(browser): harden browser tool safety boundaries 2026-07-01 05:04:41 -07:00
browser_tool.py fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428) 2026-07-24 11:45:57 -07:00
budget_config.py fix(agent): scale tool-output budget to the model context window (#23767) 2026-06-21 17:46:38 +05:30
checkpoint_manager.py fix(checkpoints): require positive volume-attachment evidence before orphan classification 2026-07-24 19:10:34 -07:00
clarify_gateway.py fix(clarify): one canonical timeout across CLI, TUI/desktop, and gateway (#69774) 2026-07-22 23:25:13 -05:00
clarify_tool.py fix(clarify): docstring — put options in choices[] only, never enumerate in question text 2026-06-19 07:34:02 -07:00
close_terminal_tool.py fix(config): accept 'on' as truthy for env flags via shared env_var_enabled helper 2026-07-02 03:00:59 -07:00
code_execution_tool.py fix(kanban): harden delegated-child mutation boundary 2026-07-23 07:33:36 -07:00
computer_use_tool.py feat(computer_use): cross-platform cua-driver (macOS/Windows/Linux) 2026-06-22 06:42:30 -07:00
credential_files.py fix(credentials): hoist read-guard import, fail closed loudly (#67665) 2026-07-20 09:21:08 -07:00
cronjob_tools.py fix(cron): reconcile external provider after a claimed direct run (#70479) 2026-07-24 13:37:04 +10:00
daemon_pool.py fix(cli): reliable interrupts, bounded exit, and exit feedback (#57000) 2026-07-02 04:20:43 -07:00
debug_helpers.py feat(moa): expose MoA presets as selectable virtual models (#46081) 2026-06-25 13:52:06 -07:00
delegate_tool.py fix(gateway): deliver kanban/delegate wake-ups to api_server sessions 2026-07-23 11:55:17 -07:00
delegation_live_log.py fix(delegation): redact credentials in live subagent transcripts 2026-07-20 06:50:31 -07:00
desktop_ui.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
discord_tool.py fix(discord): bound REST response reads 2026-07-07 02:40:04 -07:00
env_passthrough.py fix(security): strip dynamic Hermes secrets from all subprocess spawn env 2026-07-01 14:37:22 +05:30
env_probe.py fix(windows): re-fit env_probe console suppression to the temp-file _run + add no-window tests (#67690 follow-up) 2026-07-23 18:20:24 -07:00
fal_common.py refactor(image_gen): port FAL backend to plugins/image_gen/fal 2026-05-22 04:10:45 -07:00
feishu_doc_tool.py perf(cli): cut ~19s from 'hermes' cold start (skills cache + lazy Feishu + no Nous HTTP) (#22138) 2026-05-08 16:39:32 -07:00
feishu_drive_tool.py perf(cli): cut ~19s from 'hermes' cold start (skills cache + lazy Feishu + no Nous HTTP) (#22138) 2026-05-08 16:39:32 -07:00
file_operations.py fix(file-safety): distinguish safe-root write denial from credential blocks 2026-07-14 17:09:40 +05:30
file_state.py
file_tools.py refactor(terminal,file-tools): delete legacy env-side cwd tracking (step 4) 2026-07-16 00:18:38 -07:00
focus_pane_tool.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
fuzzy_match.py fix(patch): preserve file Unicode when unicode_normalized strategy matches 2026-07-01 17:48:32 +05:30
homeassistant_tool.py
hook_output_spill.py feat(hooks): spill oversized hook-injected context to disk (#20468) 2026-07-05 13:51:26 -07:00
image_generation_tool.py fix(deepinfra): harden multimodal provider routing 2026-07-14 02:59:39 +05:30
image_source.py security(vision): route local-file inputs through the shared credential-read guard 2026-07-05 00:47:54 -07:00
interrupt.py fix(interrupt): run user-approved commands from a clean interrupt slate 2026-07-06 04:58:42 -07:00
kanban_tools.py fix(gateway): deliver kanban/delegate wake-ups to api_server sessions 2026-07-23 11:55:17 -07:00
lazy_deps.py fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428) 2026-07-24 11:45:57 -07:00
managed_tool_gateway.py fix(windows): sweep remaining bare read_text/write_text sites + linter rule 2026-07-24 17:10:39 -07:00
mcp_dashboard_oauth.py fix(mcp): preserve live OAuth state during reauth 2026-07-17 04:50:47 -07:00
mcp_oauth.py fix(mcp): preserve live OAuth state during reauth 2026-07-17 04:50:47 -07:00
mcp_oauth_manager.py fix(mcp): preserve live OAuth state during reauth 2026-07-17 04:50:47 -07:00
mcp_stdio_watchdog.py fix(mcp): use direct parent identity in stdio watchdog 2026-07-18 04:15:13 -07:00
mcp_tool.py fix(mcp): use encoding_error_handler='replace' for stdio transport 2026-07-24 15:47:12 -07:00
memory_tool.py fix(memory): close second-read drift race and treat invalid UTF-8 as unreadable 2026-07-24 15:58:01 -07:00
microsoft_graph_auth.py feat(msgraph): add auth and client foundation 2026-05-08 09:27:26 -07:00
microsoft_graph_client.py fix(msgraph): stream download_to_file body instead of buffering 2026-05-08 09:27:26 -07:00
neutts_synth.py
open_preview_tool.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
openrouter_client.py
osv_check.py fix(osv_check): honor npx --package/-p install target when parsing package arg (#40567) 2026-06-06 18:30:39 -07:00
patch_parser.py fix(patch): ignore inert context-only hunks (#63678) 2026-07-13 02:42:18 -07:00
path_security.py
process_registry.py fix(process_registry): reader loop no longer hangs when an orphaned grandchild holds the stdout pipe 2026-07-24 15:59:02 -07:00
project_tools.py feat(tools): add project workspace tools 2026-06-25 16:40:27 -05:00
read_extract.py feat(read): extract notebook and office documents (#37082) 2026-06-13 14:42:51 -07:00
read_terminal_tool.py fix(config): accept 'on' as truthy for env flags via shared env_var_enabled helper 2026-07-02 03:00:59 -07:00
registry.py perf(tools): text prefilter before AST parse in tool discovery 2026-07-13 18:14:15 -04:00
schema_sanitizer.py fix(schema): preserve multi-type arrays as anyOf instead of dropping branches 2026-07-07 02:52:17 -07:00
send_message_tool.py fix(slack): prefer live send adapter and try multi-workspace tokens individually 2026-07-23 12:01:24 -07:00
session_search_tool.py fix(session-search): stop the agent restating a linked session's title 2026-07-25 12:09:36 -05:00
skill_manager_tool.py fix(curator): make the autonomous write policy consistent (#67140) 2026-07-25 19:27:17 -07:00
skill_provenance.py fix(curator): only mark agent-created for background-review sediment (#19621) 2026-05-04 02:42:16 -07:00
skill_usage.py fix(curator): make the autonomous write policy consistent (#67140) 2026-07-25 19:27:17 -07:00
skills_ast_audit.py refactor(skills): slim AST diagnostic to single entry point 2026-05-23 17:47:26 -07:00
skills_guard.py fix(skills): bind bundles to exact files and origins 2026-07-12 02:59:27 -07:00
skills_hub.py fix: add UTF-8 encoding to read_text/write_text in tools/ and agent/ 2026-07-24 17:10:39 -07:00
skills_sync.py feat(curator): surface unmanaged skills and add curator adopt 2026-07-25 18:10:24 -07:00
skills_tool.py fix(skills): scope 60-char description enforcement to the create path 2026-07-24 07:54:21 -07:00
slash_confirm.py fix(async): close unscheduled coroutines in all threadsafe bridges (#26584) 2026-05-15 14:00:01 -07:00
terminal_tool.py fix(kanban): keep delegated results in worker turn 2026-07-23 08:33:55 -07:00
thread_context.py fix(code-exec): propagate agent-turn context into tool worker threads 2026-05-29 03:44:49 -07:00
threat_patterns.py fix: bound threat-pattern/FTS5 regex input and cover V4A Move-File edits 2026-07-01 01:05:28 -07:00
tirith_security.py fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428) 2026-07-24 11:45:57 -07:00
todo_tool.py fix(compression): preserve synthetic user provenance 2026-07-22 06:56:53 -07:00
tool_backend_helpers.py feat(tools): surface the free tool pool in entitlement + setup (#36153) 2026-06-01 06:32:48 +05:30
tool_output_limits.py fix: tool_output_limits re-reads config on every call (no caching) 2026-05-31 00:50:19 -07:00
tool_result_storage.py fix: keep persisted tool results inside their storage directory 2026-06-30 16:39:41 -07:00
tool_search.py fix(tool-search): scope bridge catalog + dispatch to the session's toolsets 2026-05-29 02:04:12 -07:00
transcription_tools.py fix(tools): utf-8 decode for STT/TTS command-provider popen_kwargs 2026-07-24 15:47:12 -07:00
tts_streaming.py feat(voice): speech-interrupted latch in the TTS streaming core 2026-07-22 17:53:06 -05:00
tts_tool.py fix(tools): utf-8 decode for STT/TTS command-provider popen_kwargs 2026-07-24 15:47:12 -07:00
url_safety.py fix(url_safety): harden proxy DNS delegation — literal IPs stay fail-closed + regression tests 2026-07-24 10:37:29 -07:00
video_generation_tool.py feat(providers): Support DeepInfra as an LLM provider 2026-07-14 02:59:39 +05:30
vision_tools.py fix(security): pin DNS resolutions for SSRF-safe fetches 2026-07-23 11:44:43 -07:00
voice_mode.py fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428) 2026-07-24 11:45:57 -07:00
web_tools.py fix: add encoding="utf-8" to Path.write_text() calls (P1) 2026-07-24 17:10:39 -07:00
website_policy.py fix(website-policy): key blocklist cache on the real default config path 2026-07-04 15:08:49 -07:00
write_approval.py fix(memory,skills): repair write-approval inline prompt, gateway staging, and gateway /skills review (#43452) 2026-06-10 02:57:15 -07:00
x_search_tool.py docs(xai): clarify x_search vs xurl routing without schema cross-refs 2026-07-23 21:06:47 -07:00
xai_http.py fix: add UTF-8 encoding to read_text/write_text in tools/ and agent/ 2026-07-24 17:10:39 -07:00
xai_video_tools.py feat(xai): Imagine public-URL storage, chaining & video edit/extend 2026-06-29 21:11:58 -07:00
yuanbao_tools.py Fix unsafe gateway media path delivery 2026-05-23 01:40:35 -07:00