From bfcbd08bee3b194b315a45b58df23ba3e7f14752 Mon Sep 17 00:00:00 2001 From: BearHuddleston Date: Thu, 5 Nov 2026 23:20:24 +0000 Subject: [PATCH] fix(memory): don't count a hosted engine's prune as a withheld authority Under plugins.isolation: host every engine method is a proxy, so the __func__ identity check in _prune_would_have_run read a hosted engine's inherited prune no-op as an override. With the gate armed, the suppression count went up every tool iteration and the "withheld" warning fired once, though nothing was withheld. The prune itself is still withheld at the call site in turn_preflight, so only the count and the log were wrong. A hosted engine is now not counted. That drops the count for a hosted engine that really overrides the hook too; telling the two apart needs the plugin host to report which class defines each method, which this branch leaves to the plugin-contract work. Suggested by @unsupportedpastels in the review of #149265. --- agent/conversation_compression_checkpoint.py | 6 ++- .../test_proactive_prune_checkpoint_gate.py | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/agent/conversation_compression_checkpoint.py b/agent/conversation_compression_checkpoint.py index 84e662454f..15752b55e4 100644 --- a/agent/conversation_compression_checkpoint.py +++ b/agent/conversation_compression_checkpoint.py @@ -155,13 +155,17 @@ def _prune_would_have_run(compressor: Any, current_tokens: int) -> bool: The built-in answers via ``would_proactively_prune()``. An engine that overrides ``prune_tool_results_only`` owns its trigger policy, so occupying the hook is the precondition; the inherited ABC no-op is not an authority. A raising predicate reads as - "would have run": the call was withheld either way. + "would have run": the call was withheld either way. A hosted engine (``plugins.isolation: + host``) is not counted: its methods are all proxies, so an override can't be told from the no-op. """ from agent.context_engine import ContextEngine + from hermes_cli.plugin_host import is_hosted_object prune = getattr(compressor, "prune_tool_results_only", None) if not callable(prune) or getattr(prune, "__func__", None) is ContextEngine.prune_tool_results_only: return False + if is_hosted_object(compressor): + return False predicate = getattr(compressor, "would_proactively_prune", None) if not callable(predicate): return True diff --git a/tests/agent/test_proactive_prune_checkpoint_gate.py b/tests/agent/test_proactive_prune_checkpoint_gate.py index 5a31551710..16a41e5d44 100644 --- a/tests/agent/test_proactive_prune_checkpoint_gate.py +++ b/tests/agent/test_proactive_prune_checkpoint_gate.py @@ -208,3 +208,50 @@ def test_engine_that_never_overrode_the_prune_hook_is_not_a_suppressed_authority assert _prune_would_have_run(_InheritsTheDefault(), 400_000) is False assert _prune_would_have_run(_OccupiesTheHook(), 400_000) is True + + +def _hosted(engine, home): + """The proxy the plugin host builds for ``engine`` under ``plugins.isolation: host``, without a + host process: the host's requests go to the child runtime's own handlers in-process.""" + import itertools + import threading + from typing import Any, cast + + from agent.context_engine import ContextEngine + from hermes_cli.plugin_host import PluginHost + from hermes_cli.plugin_host_child import HostRuntime + + runtime = HostRuntime.__new__(HostRuntime) + runtime.refs, runtime.owners = {}, {} + runtime._ids, runtime._lock = itertools.count(1), threading.Lock() + ref = runtime._describe_object("engine-plugin", engine) + + def call(method, params, timeout=None): + return getattr(runtime, f"op_{method}")(params) + + host = PluginHost(SimpleNamespace(home_path=home, _plugins={})) + host._channel = cast(Any, SimpleNamespace(call=call, closed_reason=None)) + return host._object_proxy(ref, ContextEngine) + + +def test_hosted_engine_is_withheld_but_not_reported_as_a_suppressed_authority(tmp_path): + """Under ``plugins.isolation: host`` every engine method is a proxy, so identity can't tell the + inherited no-op from an override. The armed gate still withholds the call; it just doesn't count + a prune it can't prove would have run.""" + compress_only = _engine_stub() + + class _OccupiesTheHook(compress_only): + def __init__(self): + self.calls = 0 + + def prune_tool_results_only(self, messages, current_tokens=None): + self.calls += 1 + return list(messages), 1 + + messages = [{"role": "user", "content": "scan"}] + agent, overriding = _agent(True), _OccupiesTheHook() + + assert _proactive_tool_result_prune(agent, _hosted(compress_only(), tmp_path), messages, 400_000) is messages + assert _proactive_tool_result_prune(agent, _hosted(overriding, tmp_path), messages, 400_000) is messages + assert getattr(agent, "_checkpoint_gate_suppression_count", 0) == 0 + assert overriding.calls == 0