diff --git a/backend/apps/agents/manager/run/RunOptions.py b/backend/apps/agents/manager/run/RunOptions.py index c72705e4..93270aae 100644 --- a/backend/apps/agents/manager/run/RunOptions.py +++ b/backend/apps/agents/manager/run/RunOptions.py @@ -293,7 +293,15 @@ class RunOptions(AgentManagerProtocol): fenced = wrap_platform_note(f"Summary of earlier conversation (older turns compacted):\n{distilled}") history = f"{fenced}\n\n{history}" if history else fenced if history: - if isinstance(prompt_content, str): + # SYSTEM channel, not the user message (ENG-358 structural fix): a transcript recap + # inside user content is byte-for-byte what anti-distillation filters hunt, and no + # rewording makes that shape safe forever. The system prompt is platform-authored + # context by definition, and this branch only runs on fresh-session turns where the + # cached prefix is already busted, so the cache cost is zero. + p_sys = options_kwargs.get("system_prompt") + if isinstance(p_sys, dict): + p_sys["append"] = f"{p_sys.get('append', '')}\n\n{history}".strip() + elif isinstance(prompt_content, str): prompt_content = history + "\n\n" + prompt_content elif isinstance(prompt_content, list): prompt_content.insert(0, {"type": "text", "text": history}) diff --git a/backend/tests/test_content_policy_block.py b/backend/tests/test_content_policy_block.py index 96ac90dd..0bce0208 100644 --- a/backend/tests/test_content_policy_block.py +++ b/backend/tests/test_content_policy_block.py @@ -72,3 +72,17 @@ def test_second_policy_block_renders_the_terminal_card(): asyncio.run(handle_run_error(Exception(TOS_TEXT), s, "sid-pol2", TurnState(), [])) cards = [m for m in s.messages if m.role == "system"] assert len(cards) == 1 and "declined this request" in str(cards[0].content) + + +def test_recap_rides_the_system_channel_not_the_user_message(): + """The structural ENG-358 fix: history injection must land in system_prompt.append, and the + user message must stay exactly what the user (or the continuation) wrote. A transcript recap + inside user content is the anti-distillation filter's exact target shape.""" + src = open("backend/apps/agents/manager/run/RunOptions.py").read() + inject = src.split('p_sys = options_kwargs.get("system_prompt")')[1][:600] + assert 'p_sys["append"]' in inject, "system-channel injection missing" + # The user-message fallback survives only for the exotic no-system_prompt case. + before = src.split('p_sys = options_kwargs.get("system_prompt")')[0] + tail = before[-1200:] + assert "elif isinstance(prompt_content, str)" not in tail.replace( + 'p_sys = options_kwargs.get("system_prompt")', ""), "primary path must not touch prompt_content"