hermes-agent/agent
knoal 33a529538d fix(gateway): strip stale dangerous-confirmation text in user messages (#59607)
When a high-risk side effect (e.g. host restart via shutdown.exe) runs,
the user's plain-text confirmation phrase is persisted in the conversation
transcript. If the host restart killed the gateway process before the
assistant's tool result was written, the transcript tail ends on the
assistant's text response - and the dangerous confirmation text remains
in the user role.

On the next inbound message - possibly a casual 'are you there?' from
the user minutes later - the LLM sees the stale confirmation and may
interpret the new turn as a fresh re-confirmation, re-executing the
destructive action. This is the failure mode reported in #59607.

Fix:
- Add strip_stale_dangerous_confirmations() in agent/replay_cleanup.py
  that removes user messages whose content matches a known dangerous
  confirmation pattern AND whose timestamp is older than 60 seconds.
- Add is_dangerous_confirmation() helper with the matched patterns
  (i18n-aware: covers 確認強制重開機 from the original incident).
- Wire the stripper into _build_gateway_agent_history() right after the
  existing 75ed07ace strippers, so the strip chain is:
  strip_interrupted_tool_tails -> strip_dangling_tool_call_tail ->
  strip_stale_dangerous_confirmations.
- Update _build_replay_entry() to preserve the timestamp on user
  messages (it was previously dropped), since the new stripper needs it.

Complements 75ed07ace (which strips the assistant side of the broken
tail) by handling the user side: a stale plain-text confirmation that
the assistant has not yet responded to in a way the resume logic
recognises.

Failing-test-first discipline: the bug-detection test
test_stale_confirmation_text_is_stripped_on_resume fails on unfixed
code (proves the test catches the bug) and passes after the fix.
Five additional safety tests confirm no regression on:
- fresh confirmations (within expiry) are preserved
- non-confirmation text is preserved
- non-matching histories are untouched
- dangerous-pattern detection works in all cases (case, i18n, None)
- direct unit test of the strip helper

Refs: #59607
2026-07-07 14:40:32 +05:30
..
lsp fix: remove dead f-string prefixes via ruff F541 (216 sites) (#52336) 2026-07-05 13:42:46 -07:00
pet fix: remove dead f-string prefixes via ruff F541 (216 sites) (#52336) 2026-07-05 13:42:46 -07:00
secret_sources feat(secrets): adapt 1Password onto the SecretSource interface 2026-07-06 04:58:07 -07:00
transports feat(mcp): adopt mcp__server__tool naming convention 2026-07-05 13:40:21 -07:00
__init__.py fix(agent): preload jiter native parser 2026-05-28 00:20:11 -07:00
account_usage.py fix(usage): scope Codex usage pool fallback to AuthError, keep singleton token on account_id read failure 2026-07-07 12:01:33 +05:30
agent_init.py fix(agent): don't apply Codex gpt-5.5 autoraise notice when an external context engine is active 2026-07-06 12:46:20 -07:00
agent_runtime_helpers.py fix(agent): drop empty tool_calls arrays in pre-API sanitizer (#58755) 2026-07-05 21:36:37 -07:00
anthropic_adapter.py fix(auth): per-profile Anthropic OAuth file + complete port-binding platform set (#57563 salvage) (#59339) 2026-07-07 08:33:06 +00:00
async_utils.py fix(async): close unscheduled coroutines in all threadsafe bridges (#26584) 2026-05-15 14:00:01 -07:00
auxiliary_client.py fix(auxiliary): recover from stale fallback-candidate credentials instead of aborting 2026-07-06 18:12:24 -07:00
azure_identity_adapter.py feat(azure-foundry): add Microsoft Entra ID auth 2026-05-18 10:14:38 -07:00
background_review.py fix(background-review): guard summarize against list-shaped tool responses (#59437) 2026-07-06 03:28:30 -07:00
bedrock_adapter.py fix(bedrock): check boto3 version >= 1.34.59 before using converse_stream 2026-06-15 05:25:17 -07:00
billing_view.py feat(billing): /billing terminal billing — interactive TUI + CLI client (#45449) 2026-06-19 01:53:32 +05:30
bounded_response.py fix(agents): bound streaming error-response body reads 2026-07-05 14:00:20 -07:00
browser_provider.py fix(browser): self-review pass — dead-import, log levels, future-proofing 2026-05-17 04:04:15 -07:00
browser_registry.py style: restore PEP8 blank-line separation after dead-code removal 2026-05-29 04:22:27 -07:00
chat_completion_helpers.py fix(poolside): handle integer finish_reason and tool_call id 2026-07-04 15:44:50 -07:00
codex_responses_adapter.py fix(codex): route commentary-phase preamble text to reasoning channel (fixes #41293) 2026-07-05 06:29:45 -07:00
codex_runtime.py fix(codex): stream commentary deltas through the reasoning channel 2026-07-05 06:29:45 -07:00
coding_context.py feat(agent): add configurable coding_instructions 2026-06-30 00:59:59 -05:00
context_breakdown.py feat(desktop): add context usage breakdown popover 2026-06-29 09:18:10 -04:00
context_compressor.py [verified] fix: account for codex replay in compression tail budget 2026-07-06 12:43:59 -07:00
context_engine.py fix(context): clamp -1 post-compression sentinel in sibling status paths 2026-07-01 13:36:50 +05:30
context_references.py fix(security): anchor @file context refs to canonical read deny-list 2026-07-01 02:43:49 -07:00
conversation_compression.py refactor(compression): reuse _fresh_compaction_message_copy in user-turn guard 2026-07-04 21:04:27 +05:30
conversation_loop.py refactor(retry): single-source Z.AI overload short-attempts + drop change-detector assert 2026-07-07 11:57:01 +05:30
copilot_acp_client.py fix(agent): stream copilot ACP chat completions 2026-06-28 22:52:51 -07:00
credential_persistence.py feat(auth): make xAI Grok OAuth device-code-only, drop loopback login 2026-07-02 13:17:41 -07:00
credential_pool.py fix(secrets): make 1Password bootstrap token reliable outside systemd 2026-07-06 04:58:07 -07:00
credential_sources.py feat(auth): make xAI Grok OAuth device-code-only, drop loopback login 2026-07-02 13:17:41 -07:00
credits_tracker.py feat(billing): /credits command — balance + portal top-up handoff (#44776) 2026-06-12 08:51:10 +00:00
curator.py fix(curator): never archive cron-referenced skills + floor use=0 pruning (#54443) 2026-06-28 15:10:21 -07:00
curator_backup.py fix: remove dead f-string prefixes via ruff F541 (216 sites) (#52336) 2026-07-05 13:42:46 -07:00
display.py feat(display): show file_path in skill_view tool progress lines (#60079) 2026-07-07 01:07:49 -07:00
error_classifier.py fix(error-classifier): retry HTTP 408 as timeout instead of aborting as format_error 2026-07-06 01:56:09 -07:00
errors.py fix(agent,gateway,doctor): add SSL CA cert bundle fail-fast guard 2026-06-13 21:14:32 -07:00
file_safety.py fix(image-gen): route local-input credential guard through one shared chokepoint + cover xai (#57698) 2026-07-03 18:47:53 +05:30
gemini_native_adapter.py fix(agents): bound streaming error-response body reads 2026-07-05 14:00:20 -07:00
gemini_schema.py chore: remove unused imports and dead locals (ruff F401, F841) (#17010) 2026-04-28 06:46:45 -07:00
i18n.py fix(packaging): ship locales/ i18n catalogs in wheel, sdist, and Nix (#38383) 2026-06-03 12:00:27 -07:00
image_gen_provider.py feat(image-gen): add image-to-image / editing to image_generate (#48705) 2026-06-18 22:13:07 -07:00
image_gen_registry.py fix(plugins): filter resolution by is_available() in web + image_gen registries 2026-05-13 22:31:28 -07:00
image_routing.py Guard native image routing with file safety 2026-07-05 03:15:03 -07:00
insights.py refactor(insights): drop dead pricing/duration wrappers, call usage_pricing directly (#40618) 2026-06-07 18:33:20 -07:00
iteration_budget.py refactor(run_agent): extract OpenAI proxy, safe stdio, IterationBudget 2026-05-16 17:59:32 -07:00
jiter_preload.py fix(agent): preload jiter native parser 2026-05-28 00:20:11 -07:00
learn_prompt.py fix(learn): honor requirements mixed with sources in /learn requests (#55956) 2026-06-30 16:56:01 -07:00
learning_graph.py fix(learning_graph): guard non-dict metadata so /journey can't crash 2026-07-01 16:25:48 -05:00
learning_graph_render.py fix: cover remaining GNU-only %-d strftime site in learning graph render 2026-07-05 00:59:35 -07:00
learning_mutations.py refactor(journey): route memory mutations through MemoryStore atomic I/O 2026-06-30 15:16:21 -05:00
lmstudio_reasoning.py feat(agent): add lmstudio integration 2026-04-28 12:27:36 -07:00
manual_compression_feedback.py fix(compression): include system prompt + tool schemas in token estimates (#18265) 2026-04-30 23:03:54 -07:00
markdown_tables.py fix(cli): vertical fallback for markdown tables wider than terminal (#23948) 2026-05-11 16:49:13 -07:00
memory_manager.py fix(cli): reliable interrupts, bounded exit, and exit feedback (#57000) 2026-07-02 04:20:43 -07:00
memory_provider.py fix(backup): capture memory-provider state stored outside HERMES_HOME (#50325) 2026-06-21 12:03:46 -07:00
message_content.py fix(openviking): preserve structured sync attribution 2026-06-19 15:23:41 +08:00
message_sanitization.py fix(agent): close tool-call sequence on all interrupt aborts, not just finalize_turn 2026-06-25 12:24:34 -05:00
moa_loop.py fix(moa): apply prompt-caching decoration to the aggregator's one-shot synthesis call 2026-07-04 20:59:02 +05:30
moa_trace.py fix(moa): capture streamed aggregator output into full-turn traces (#56312) 2026-07-01 04:07:46 -07:00
model_metadata.py perf: cut first-turn time-to-first-token by ~80% (all platforms) (#59332) 2026-07-05 21:37:33 -07:00
models_dev.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
moonshot_schema.py fix(moonshot): handle union type arrays in tool schemas 2026-06-13 05:51:41 -07:00
nous_rate_guard.py codebase: add encoding='utf-8' to all bare open() calls (PLW1514) 2026-05-08 14:27:40 -07:00
onboarding.py fix(config): close unreadable-overwrite bug class at a single chokepoint 2026-07-05 23:00:34 +05:30
oneshot.py feat(agent): one-shot LLM helper + llm.oneshot gateway RPC (#51261) 2026-06-23 08:01:50 +00:00
plugin_llm.py feat(plugins): run any LLM call from inside a plugin via ctx.llm (#23194) 2026-05-10 07:09:28 -07:00
portal_tags.py feat(nous): unified client=hermes-client-v<version> tag on every Portal request (#24779) 2026-05-12 20:49:20 -07:00
process_bootstrap.py fix(agent): apply pool-level keepalive to the process_bootstrap sibling builder 2026-07-05 03:14:55 -07:00
prompt_builder.py fix(agent): limit .hermes.md parent walk to git repos only 2026-06-28 20:46:32 -07:00
prompt_caching.py fix(prompt-caching): align _can_carry_marker with last-part-dict marking 2026-07-04 13:54:27 +05:30
rate_limit_tracker.py refactor: remove dead code — 1,784 lines across 77 files (#9180) 2026-04-13 16:32:04 -07:00
reasoning_timeouts.py fix(agent): detect thinking-timeout for reasoning models and surface actionable guidance instead of misleading file-write advice 2026-06-25 19:00:48 -07:00
redact.py fix(redact): skip env-lookup exception for JSON/YAML config field redaction 2026-07-05 13:58:19 -07:00
replay_cleanup.py fix(gateway): strip stale dangerous-confirmation text in user messages (#59607) 2026-07-07 14:40:32 +05:30
retry_utils.py refactor(retry): single-source Z.AI overload short-attempts + drop change-detector assert 2026-07-07 11:57:01 +05:30
runtime_cwd.py fix(desktop): stabilize project folder sessions (#37586) 2026-06-02 20:23:09 +00:00
secret_scope.py feat(gateway): multiplex phase 2 — fail-closed profile credential isolation (Workstream A) 2026-06-19 07:34:15 -07:00
shell_hooks.py feat(hooks): spill oversized hook-injected context to disk (#20468) 2026-07-05 13:51:26 -07:00
skill_bundles.py fix(gateway): apply platform-disabled skill gate to bundle invocations (#59156) 2026-07-05 14:48:11 -07:00
skill_commands.py fix(skills): apply disabled-skill gate to CLI/TUI preloaded skills 2026-07-06 02:43:37 -07:00
skill_preprocessing.py fix(windows): hide console-window flash on backend git/gh/wmic/bash subprocess spawns 2026-06-28 05:28:45 -07:00
skill_utils.py fix(curator): protect external skills from background curation 2026-06-25 22:03:02 -07:00
ssl_guard.py fix(ssl): align guard docs and escape hatch 2026-06-13 21:14:32 -07:00
ssl_verify.py fix(agent): honor custom CA certs on aux client + harden TLS resolution 2026-07-02 04:51:56 +05:30
stream_diag.py feat(agent): buffer retry/fallback status, surface only on terminal failure (#33816) 2026-05-28 04:53:27 -07:00
subdirectory_hints.py fix(subdirectory_hints): catch RuntimeError from Path.expanduser() 2026-07-01 04:55:15 -07:00
system_prompt.py feat(computer_use): cross-platform cua-driver (macOS/Windows/Linux) 2026-06-22 06:42:30 -07:00
think_scrubber.py fix(agent): stateful streaming scrubber for reasoning-block leaks (#17924) (#20184) 2026-05-05 04:33:38 -07:00
thinking_timeout_guidance.py fix(agent): detect thinking-timeout for reasoning models and surface actionable guidance instead of misleading file-write advice 2026-06-25 19:00:48 -07:00
thread_scoped_output.py fix(bg-review): scope stdout/stderr silencing to the worker thread (#55966) 2026-06-30 17:28:33 -07:00
title_generator.py fix(title_generator): strip think blocks from LLM output before extracting title 2026-07-01 04:18:48 -07:00
tool_dispatch_helpers.py fix(agent): wrap list-type untrusted content in untrusted_tool_result 2026-07-01 02:44:09 -07:00
tool_executor.py Revert "Merge pull request #58698 from kshitijk4poor/feat/pre-tool-call-approve-escalation" 2026-07-06 02:36:52 +05:30
tool_guardrails.py fix: add recovery hints to loop guard warnings 2026-05-19 00:12:12 -07:00
tool_result_classification.py fix: classify landed file mutations with diagnostics 2026-05-13 06:46:23 -07:00
trajectory.py Refactor Terminal and AIAgent cleanup 2026-02-21 22:31:43 -08:00
transcription_provider.py feat(stt): add register_transcription_provider() plugin hook 2026-05-25 01:41:19 -07:00
transcription_registry.py feat(stt): add register_transcription_provider() plugin hook 2026-05-25 01:41:19 -07:00
tts_provider.py feat(tts): add register_tts_provider() plugin hook (closes #30398) 2026-05-24 18:04:54 -07:00
tts_registry.py feat(tts): add register_tts_provider() plugin hook (closes #30398) 2026-05-24 18:04:54 -07:00
turn_context.py perf: cut first-turn time-to-first-token by ~80% (all platforms) (#59332) 2026-07-05 21:37:33 -07:00
turn_finalizer.py fix(agent): persist recovered final responses 2026-07-01 03:34:49 -07:00
turn_retry_state.py feat(vertex): add Google Vertex AI provider for Gemini (OAuth2) 2026-07-01 05:25:33 -07:00
usage_pricing.py fix(usage): capture reasoning_tokens from completion_tokens_details on chat_completions (#57340) 2026-07-02 13:52:42 -07:00
verification_evidence.py feat(agent): recognize focused ad-hoc verification scripts 2026-06-24 23:03:45 -05:00
verification_stop.py feat(agent): restore surface-aware "auto" default for verify_on_stop 2026-06-30 01:43:08 -05:00
verify_hooks.py feat(agent): add pre_verify hook and verify-on-stop coding guidance 2026-06-30 00:59:29 -05:00
vertex_adapter.py security(vertex): route credential/project/region resolution through the profile secret scope 2026-07-02 06:07:56 +05:30
video_gen_provider.py feat(video_gen): unified video_generate tool with pluggable provider backends (#25126) 2026-05-13 16:39:41 -07:00
video_gen_registry.py feat(video_gen): unified video_generate tool with pluggable provider backends (#25126) 2026-05-13 16:39:41 -07:00
web_search_provider.py fix(web): widen config-aware env resolution to exa/parallel/tavily/brave-free providers 2026-07-06 02:42:24 -07:00
web_search_registry.py fix(web): correct 'disabled plugin' diagnosis for web backends (#59573) 2026-07-06 04:38:17 -07:00