Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions agent/agent_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,17 @@ def init_agent(

# Memory provider plugin (external β€” one at a time, alongside built-in)
# Reads memory.provider from config to select which plugin to activate.
# memory.recall_indicator gates the deterministic recall status line
# ("πŸ‘οΈ Hindsight β€” recalled 3 memories") emitted when external memory is
# auto-recalled before a turn. Default true preserves current behaviour;
# false keeps recall internal (context still injected, status suppressed).
agent._recall_indicator_enabled = True
try:
agent._recall_indicator_enabled = bool(
((_agent_cfg or {}).get("memory") or {}).get("recall_indicator", True)
)
except Exception:
agent._recall_indicator_enabled = True
agent._memory_manager = None
if not skip_memory:
try:
Expand Down
9 changes: 7 additions & 2 deletions agent/turn_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,11 +1287,16 @@ def build_turn_context(
# Deterministic, model-independent recall indicator: when memory was
# actually injected this turn, tell the user β€” don't rely on the model
# to surface it. Rendered by Hermes (via _emit_status), so it always
# shows and can't be silently dropped by the model.
# shows and can't be silently dropped by the model. Gate: the
# memory.recall_indicator config flag (agent._recall_indicator_enabled,
# set at init, default true) lets users keep recall internal β€” memory
# still injects into context, but the status line is suppressed.
if ext_prefetch_cache:
try:
_recall_indicator = agent._memory_manager.describe_recall()
if _recall_indicator:
if _recall_indicator and getattr(
agent, "_recall_indicator_enabled", True
):
agent._emit_status(_recall_indicator)
except Exception:
pass
Expand Down
7 changes: 7 additions & 0 deletions cli-config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,13 @@ memory:
# For exit/reset, only fires if the session had at least this many user turns.
flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)

# Deterministic recall indicator: when an external memory provider
# (memory.provider) auto-recalls memory before a turn, Hermes shows a status
# line (e.g. "πŸ‘οΈ Hindsight β€” recalled 3 memories") on the CLI and messaging
# platforms so you SEE memory was used. Set false to keep recall internal β€”
# memory still injects into context, but the status line is suppressed.
recall_indicator: true

# =============================================================================
# Session Reset Policy (Messaging Platforms)
# =============================================================================
Expand Down
6 changes: 6 additions & 0 deletions hermes_cli/config_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -1778,6 +1778,12 @@
# "hindsight", "holographic", "retaindb", "byterover".
# Only ONE external provider is allowed at a time.
"provider": "",
# Deterministic recall indicator: when an external provider auto-recalls
# memory before a turn, Hermes shows a status line (e.g.
# "πŸ‘οΈ Hindsight β€” recalled 3 memories") so you SEE memory was used.
# Set false to keep recall internal: memory still injects into context,
# but the status line is suppressed on every surface (CLI + gateway).
"recall_indicator": True,
},

# Subagent delegation β€” override the provider:model used by delegate_task
Expand Down
21 changes: 21 additions & 0 deletions tests/agent/test_turn_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,27 @@ def test_recall_indicator_skipped_when_nothing_injected():
assert "πŸ‘οΈ" not in str(call)


def test_recall_indicator_suppressed_when_config_disabled():
"""memory.recall_indicator: false keeps recall internal β€” no status line.

Memory still injects into context (prefetch_all runs and returns content),
but the deterministic indicator must not be emitted on any surface.
"""
agent = _FakeAgent()
agent._emit_status = MagicMock()
agent._recall_indicator_enabled = False
mm = MagicMock()
mm.prefetch_all.return_value = "- recalled fact"
mm.describe_recall.return_value = "πŸ‘οΈ Hindsight β€” recalled 2 memories"
agent._memory_manager = mm

_build(agent, user_message="what did we decide about the deploy pipeline?")

mm.describe_recall.assert_called_once()
for call in agent._emit_status.call_args_list:
assert "πŸ‘οΈ" not in str(call)


def test_ensure_db_session_runs_after_system_prompt_restore():
"""Regression for #45499.

Expand Down