NousResearch/hermes-agent

Codex Responses final_answer vs incomplete status mapping

Cluster 27988 · agent. Data as of

Nothing open on GitHub. Fix merged in #45814 on 13 Jun 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #45814. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #45814. That is separate from the fix event.
Still open
Nothing open on GitHub.

The members concern the Codex Responses adapter's handling of a final_answer phase when the top-level response status is incomplete, including a report of finish_reason=incomplete on Azure Foundry. Two members are titled as fixes for this behavior, and the third describes the issue they appear to address.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #45814 fix(codex-responses): let final_answer complete top-level incomplete responses

    merged to main

    files: agent/codex_responses_adapter.py, tests/run_agent/test_run_agent_codex_responses.py

    Show the diff
    • agent/codex_responses_adapter.py
      @@ -1081,6 +1081,7 @@ def _normalize_codex_response(
           message_items_raw: List[Dict[str, Any]] = []
           tool_calls: List[Any] = []
           has_incomplete_items = response_status in {"queued", "in_progress", "incomplete"}
      +    saw_streaming_or_item_incomplete = response_status in {"queued", "in_progress"}
           saw_commentary_phase = False
           saw_final_answer_phase = False
           saw_reasoning_item = False
      @@ -1095,6 +1096,7 @@ def _normalize_codex_response(
       
               if item_status in {"queued", "in_progress", "incomplete"}:
                   has_incomplete_items = True
      +            saw_streaming_or_item_incomplete = True
       
               if item_type == "message":
                   item_phase = getattr(item, "phase", None)
      @@ -1252,7 +1254,9 @@ def _normalize_codex_response(
               finish_reason = "tool_calls"
           elif leaked_tool_call_text:
               finish_reason = "incomplete"
      -    elif has_incomplete_items or (saw_commentary_phase and not saw_final_answer_phase):
      +    elif saw_streaming_or_item_incomplete:
      +        finish_reason = "incomplete"
      +    elif (has_incomplete_items or saw_commentary_phase) and not saw_final_answer_phase:
               finish_reason = "incomplete"
           elif (reasoning_items_raw or reasoning_parts or saw_reasoning_item) and not final_text:
               # Response contains only reasoning (encrypted thinking state and/or
    • tests/run_agent/test_run_agent_codex_responses.py
      @@ -154,6 +154,22 @@ def _codex_ack_message_response(text: str):
           )
       
       
      +def _codex_final_answer_with_top_level_incomplete_response(text: str):
      +    return SimpleNamespace(
      +        output=[
      +            SimpleNamespace(
      +                type="message",
      +                phase="final_answer",
      +                status="completed",
      +                content=[SimpleNamespace(type="output_text", text=text)],
      +            )
      +        ],
      +        usage=SimpleNamespace(input_tokens=4, output_tokens=2, total_tokens=6),
      +        status="incomplete",
      +        model="gpt-5.4",
      +    )
      +
      +
       class _FakeCreateStream:
           """Iterable-only fake for ``responses.create(stream=True)`` outputs.
       
      @@ -1351,6 +1367,92 @@ def test_normalize_codex_response_marks_commentary_only_message_as_incomplete(mo
           assert "inspect the repository" in (assistant_message.content or "")
       
       
      +def test_normalize_codex_response_final_answer_overrides_top_level_incomplete(monkeypatch):
      +    from agent.codex_responses_adapter import _normalize_codex_response
      +
      +    assistant_message, finish_reason = _normalize_codex_response(
      +        _codex_final_answer_with_top_level_incomplete_response(
      +            "Briefly:\n\n- I'm Ramsay, your assistant."
      +        )
      +    )
      +
      +    assert finish_reason == "stop"
      +    assert "Ramsay" in (assistant_message.content or "")
      +
      +
      +def test_normalize_codex_response_top_level_incomplete_without_final_answer_stays_incomplete(monkeypatch):
      +    from agent.codex_responses_adapter import _normalize_codex_response
      +
      +    response = SimpleNamespace(
      +        output=[
      +            SimpleNamespace(
      +                type="message",
      +                status="completed",
      +                content=[SimpleNamespace(type="output_text", text="Partial...")],
      +            )
      +        ],
      +        usage=SimpleNamespace(input_tokens=4, output_tokens=2, total_tokens=6),
      +        status="incomplete",
      +        model="gpt-5.4",
      +    )
      +
      +    _, finish_reason = _normalize_codex_response(response)
      +
      +    assert finish_reason == "incomplete"
      +
      +
      [redacted:email]("top_level_status", ["queued", "in_progress"])
      +def test_normalize_codex_response_final_answer_does_not_override_streaming_status(
      +    monkeypatch, top_level_status
      +):
      +    from agent.codex_responses_adapter import _normalize_codex_response
      +
      +    response = SimpleNamespace(
      +        output=[
      +            SimpleNamespace(
      +                type="message",
      +                phase="final_answer",
      +                status="completed",
      +                content=[SimpleNamespace(type="output_text", text="Interim answer.")],
      +            )
      +        ],
      +        usage=SimpleNamespace(input_tokens=4, output_tokens=2, total_tokens=6),
      +        status=top_level_status,
      +        model="gpt-5.4",
      +    )
      +
      +    _, finish_reason = _normalize_codex_response(response)
      +
      +    assert finish_reason == "incomplete"
      +
      +
      +def test_normalize_codex_response_final_answer_does_not_override_per_item_in_progress(monkeypatch):
      +    from agent.codex_responses_adapter import _normalize_codex_response
      +
      +    response = SimpleNamespace(
      +        output=[
      +            SimpleNamespace(
      +                type="message",
      +                phase="final_answer",
      +                status="completed",
      +                content=[SimpleNamespace(type="output_text", text="Partial final.")],
      +            ),
      +            SimpleNamespace(
      +                type="message",
      +                status="in_progress",
      +                content=[SimpleNamespace(type="output_text", text="")],
      +            ),
      +        ],
      +        usage=SimpleNamespace(input_tokens=4, output_tokens=2, total_tokens=6),
      +        status="completed",
      +        model="gpt-5.4",
      +    )
      +
      +    _, finish_reason = _normalize_codex_response(response)
      +
      +    assert finish_reason == "incomplete"
      +
      +
       def test_normalize_codex_response_preserves_message_status_for_replay(monkeypatch):
           """Incomplete Codex output messages must not be replayed as completed."""
           agent = _build_agent(monkeypatch)

1 PR closed without merging

From the close comments: merged via another change 1

  • #28039 fix(codex-responses): final_answer phase overrides top-level incomplete status (#27988) · merged via another change → 7d11fa4, #45814
Closed issues · 1
  • Closed#27988Codex Responses adapter maps complete final_answer to finish_reason=incomplete when top-level response.status is incomplete on Azure Foundry
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
agent
Merged PRs
#45814
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
13 Jun 2026

For your agent

The same facts, typed.