diff --git a/run_agent.py b/run_agent.py index 48382389e..efaeba829 100644 --- a/run_agent.py +++ b/run_agent.py @@ -3589,7 +3589,12 @@ class AIAgent: item_id = ri.get("id") if item_id and item_id in seen_item_ids: continue - items.append(ri) + # Strip the "id" field — with store=False the + # Responses API cannot look up items by ID and + # returns 404. The encrypted_content blob is + # self-contained for reasoning chain continuity. + replay_item = {k: v for k, v in ri.items() if k != "id"} + items.append(replay_item) if item_id: seen_item_ids.add(item_id) has_codex_reasoning = True @@ -3730,8 +3735,10 @@ class AIAgent: continue seen_ids.add(item_id) reasoning_item = {"type": "reasoning", "encrypted_content": encrypted} - if isinstance(item_id, str) and item_id: - reasoning_item["id"] = item_id + # Do NOT include the "id" in the outgoing item — with + # store=False (our default) the API tries to resolve the + # id server-side and returns 404. The id is still used + # above for local deduplication via seen_ids. summary = item.get("summary") if isinstance(summary, list): reasoning_item["summary"] = summary diff --git a/tests/run_agent/test_run_agent_codex_responses.py b/tests/run_agent/test_run_agent_codex_responses.py index 785d85886..2b2295565 100644 --- a/tests/run_agent/test_run_agent_codex_responses.py +++ b/tests/run_agent/test_run_agent_codex_responses.py @@ -1249,13 +1249,17 @@ def test_chat_messages_to_responses_input_deduplicates_reasoning_ids(monkeypatch ] items = agent._chat_messages_to_responses_input(messages) - reasoning_ids = [it["id"] for it in items if it.get("type") == "reasoning"] - # rs_aaa should appear only once (first occurrence kept) - assert reasoning_ids.count("rs_aaa") == 1 - # rs_bbb and rs_ccc should each appear once - assert reasoning_ids.count("rs_bbb") == 1 - assert reasoning_ids.count("rs_ccc") == 1 - assert len(reasoning_ids) == 3 + reasoning_items = [it for it in items if it.get("type") == "reasoning"] + # Dedup: rs_aaa appears in both turns but should only be emitted once. + # 3 unique items total: enc_1 (from rs_aaa), enc_2 (rs_bbb), enc_3 (rs_ccc). + assert len(reasoning_items) == 3 + encrypted = [it["encrypted_content"] for it in reasoning_items] + assert encrypted.count("enc_1") == 1 + assert "enc_2" in encrypted + assert "enc_3" in encrypted + # IDs must be stripped — with store=False the API 404s on id lookups. + for it in reasoning_items: + assert "id" not in it def test_preflight_codex_input_deduplicates_reasoning_ids(monkeypatch): @@ -1272,7 +1276,11 @@ def test_preflight_codex_input_deduplicates_reasoning_ids(monkeypatch): normalized = agent._preflight_codex_input_items(raw_input) reasoning_items = [it for it in normalized if it.get("type") == "reasoning"] - reasoning_ids = [it["id"] for it in reasoning_items] - assert reasoning_ids.count("rs_xyz") == 1 - assert reasoning_ids.count("rs_zzz") == 1 + # rs_xyz duplicate should be collapsed to one item; rs_zzz kept. assert len(reasoning_items) == 2 + encrypted = [it["encrypted_content"] for it in reasoning_items] + assert encrypted.count("enc_a") == 1 + assert "enc_b" in encrypted + # IDs must be stripped — with store=False the API 404s on id lookups. + for it in reasoning_items: + assert "id" not in it