Summary
External memory providers currently receive sync_all(original_user_message, final_response) for a turn whenever final_response and original_user_message are present. The sync path does not appear to exclude interrupted turns.
This can cause an external memory provider to persist an incomplete/interrupted exchange as if it were a completed conversational turn.
Why this matters
Memory providers need a clean boundary between:
- a completed assistant turn that is safe to persist as conversational evidence; and
- an interrupted/reset/cancelled turn whose final state is not reliable durable memory.
If an interrupted turn is mirrored into an external memory backend, downstream recall can treat partial assistant output, partial tool processing, or aborted work as durable conversation truth.
Current behavior in vanilla Hermes
In run_agent.py, after clear_interrupt() has been called, the external memory sync path is guarded by self._memory_manager and final_response and original_user_message, but not by the interrupted state itself:
# External memory provider: sync the completed turn + queue next prefetch.
# Use original_user_message (clean input) — user_message may contain
# injected skill content that bloats / breaks provider queries.
if self._memory_manager and final_response and original_user_message:
try:
self._memory_manager.sync_all(original_user_message, final_response)
self._memory_manager.queue_prefetch_all(original_user_message)
except Exception:
pass
Expected behavior
External memory providers should not be asked to persist interrupted turns as completed conversation turns.
A minimal fix could be one of:
- preserve an
interrupted / was_interrupted local before clearing interrupt state and gate sync on not was_interrupted; or
- expose a structured turn status to memory providers so they can skip non-final turns.
Suggested acceptance criteria
- Interrupted turns do not call
MemoryManager.sync_all(...) as completed turns.
- Normal completed turns still sync as before.
- There is a regression test for interrupted-turn memory sync behavior.
- The fix is generic and does not depend on any specific external memory provider.
Summary
External memory providers currently receive
sync_all(original_user_message, final_response)for a turn wheneverfinal_responseandoriginal_user_messageare present. The sync path does not appear to exclude interrupted turns.This can cause an external memory provider to persist an incomplete/interrupted exchange as if it were a completed conversational turn.
Why this matters
Memory providers need a clean boundary between:
If an interrupted turn is mirrored into an external memory backend, downstream recall can treat partial assistant output, partial tool processing, or aborted work as durable conversation truth.
Current behavior in vanilla Hermes
In
run_agent.py, afterclear_interrupt()has been called, the external memory sync path is guarded byself._memory_manager and final_response and original_user_message, but not by the interrupted state itself:Expected behavior
External memory providers should not be asked to persist interrupted turns as completed conversation turns.
A minimal fix could be one of:
interrupted/was_interruptedlocal before clearing interrupt state and gate sync onnot was_interrupted; orSuggested acceptance criteria
MemoryManager.sync_all(...)as completed turns.