From d29e9ff2e78d4e244c907b7dddb66df5e5d01ecd Mon Sep 17 00:00:00 2001 From: Reese Date: Wed, 29 Apr 2026 20:40:42 +0000 Subject: [PATCH] fix(codex): filter synthetic AGENTS startup messages --- .../codex/scripts/lib/content.py | 23 +++++++ .../codex/tests/test_content.py | 69 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/hindsight-integrations/codex/scripts/lib/content.py b/hindsight-integrations/codex/scripts/lib/content.py index 065e37f3d5..5237369634 100644 --- a/hindsight-integrations/codex/scripts/lib/content.py +++ b/hindsight-integrations/codex/scripts/lib/content.py @@ -45,6 +45,25 @@ def strip_memory_tags(content: str) -> str: return content +def is_synthetic_codex_user_message(content: str) -> bool: + """Return True for setup messages Codex persists as user chat. + + Codex records AGENTS.md startup instructions as a normal user message in + rollout JSONL. Retaining those messages teaches Hindsight about hook and + agent rules instead of the user's work, and can create very large retain + payloads in long-running sessions. + """ + if not isinstance(content, str): + return False + + stripped = content.lstrip() + return ( + stripped.startswith("# AGENTS.md instructions for ") + and "" in stripped + and "" in stripped + ) + + # --------------------------------------------------------------------------- # Transcript reading # --------------------------------------------------------------------------- @@ -118,6 +137,8 @@ def _read_transcript_text(transcript_path: str) -> list: if t: text_parts.append(t) text = "\n".join(text_parts).strip() + if role == "user" and is_synthetic_codex_user_message(text): + continue if text: messages.append({"role": role, "content": text}) # Flat format (testing / future compatibility) @@ -187,6 +208,8 @@ def _flush_assistant(): if role == "user": _flush_assistant() text = _extract_text_from_content_blocks(payload.get("content", [])) + if is_synthetic_codex_user_message(text): + continue if text: messages.append({"role": "user", "content": [{"type": "text", "text": text}]}) elif role == "assistant": diff --git a/hindsight-integrations/codex/tests/test_content.py b/hindsight-integrations/codex/tests/test_content.py index cdfd6ecd00..4626eb9f49 100644 --- a/hindsight-integrations/codex/tests/test_content.py +++ b/hindsight-integrations/codex/tests/test_content.py @@ -9,6 +9,7 @@ from lib.content import ( compose_recall_query, format_memories, + is_synthetic_codex_user_message, prepare_retention_transcript, read_transcript, slice_last_turns_by_user_boundary, @@ -51,6 +52,16 @@ def test_strips_multiline_block(self): assert strip_memory_tags(raw).strip() == "" +class TestSyntheticCodexUserMessage: + def test_detects_agents_startup_instructions(self): + raw = "# AGENTS.md instructions for /home/coder/project\n\n\nremember things\n" + assert is_synthetic_codex_user_message(raw) + + def test_does_not_match_normal_agents_discussion(self): + raw = "We should update AGENTS.md instructions for this repo." + assert not is_synthetic_codex_user_message(raw) + + # --------------------------------------------------------------------------- # read_transcript — flat format # --------------------------------------------------------------------------- @@ -160,6 +171,35 @@ def test_skips_invalid_roles(self, tmp_path): msgs = read_transcript(path) assert len(msgs) == 0 + def test_skips_codex_agents_startup_instructions(self, tmp_path): + entries = [ + { + "type": "response_item", + "payload": { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_text", + "text": "# AGENTS.md instructions for /home/coder/project\n\n\nsetup rules\n", + } + ], + }, + }, + { + "type": "response_item", + "payload": { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "actual user request"}], + }, + }, + ] + path = _write_jsonl(tmp_path, entries) + msgs = read_transcript(path) + assert len(msgs) == 1 + assert msgs[0]["content"] == "actual user request" + def test_skips_blank_lines_gracefully(self, tmp_path): f = tmp_path / "transcript.jsonl" f.write_text('\n{"role": "user", "content": "hi"}\n\n{"role": "assistant", "content": "hey"}\n') @@ -398,6 +438,35 @@ def test_reads_messages_as_structured_blocks(self, tmp_path): assert msgs[1]["role"] == "assistant" assert msgs[1]["content"] == [{"type": "text", "text": "hi there"}] + def test_skips_codex_agents_startup_instructions(self, tmp_path): + entries = [ + { + "type": "response_item", + "payload": { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_text", + "text": "# AGENTS.md instructions for /home/coder/project\n\n\nsetup rules\n", + } + ], + }, + }, + { + "type": "response_item", + "payload": { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "actual user request"}], + }, + }, + ] + path = _write_jsonl(tmp_path, entries) + msgs = read_transcript(path, include_tool_calls=True) + assert len(msgs) == 1 + assert msgs[0]["content"] == [{"type": "text", "text": "actual user request"}] + def test_reads_function_call(self, tmp_path): entries = [ {