NousResearch/hermes-agent

ACP adapter missing usage field population fix

Cluster 7007. Data as of

Nothing open on GitHub. Fix merged in #7086 on 10 Apr 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 #7086. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #7086. That is separate from the fix event.
Still open
Nothing open on GitHub.

A bug was identified where PromptResponse.usage was always None in the ACP adapter due to top-level result keys being missed. Related fixes address populating the usage field correctly from top-level result fields.

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.

  • #7086 fix(acp): populate PromptResponse usage from top-level result fields

    merged to main

    files: acp_adapter/server.py, tests/acp/test_server.py

    Show the diff
    • acp_adapter/server.py
      @@ -451,14 +451,13 @@ def _run_agent() -> dict:
                   await conn.session_update(session_id, update)
       
               usage = None
      -        usage_data = result.get("usage")
      -        if usage_data and isinstance(usage_data, dict):
      +        if any(result.get(key) is not None for key in ("prompt_tokens", "completion_tokens", "total_tokens")):
                   usage = Usage(
      -                input_tokens=usage_data.get("prompt_tokens", 0),
      -                output_tokens=usage_data.get("completion_tokens", 0),
      -                total_tokens=usage_data.get("total_tokens", 0),
      -                thought_tokens=usage_data.get("reasoning_tokens"),
      -                cached_read_tokens=usage_data.get("cached_tokens"),
      +                input_tokens=result.get("prompt_tokens", 0),
      +                output_tokens=result.get("completion_tokens", 0),
      +                total_tokens=result.get("total_tokens", 0),
      +                thought_tokens=result.get("reasoning_tokens"),
      +                cached_read_tokens=result.get("cache_read_tokens"),
                   )
       
               stop_reason = "cancelled" if state.cancel_event and state.cancel_event.is_set() else "end_turn"
    • tests/acp/test_server.py
      @@ -410,6 +410,37 @@ async def test_prompt_sends_final_message_update(self, agent):
               update = last_call[1].get("update") or last_call[0][1]
               assert update.session_update == "agent_message_chunk"
       
      +    @pytest.mark.asyncio
      +    async def test_prompt_populates_usage_from_top_level_run_conversation_fields(self, agent):
      +        """ACP should map top-level token fields into PromptResponse.usage."""
      +        new_resp = await agent.new_session(cwd=".")
      +        state = agent.session_manager.get_session(new_resp.session_id)
      +
      +        state.agent.run_conversation = MagicMock(return_value={
      +            "final_response": "usage attached",
      +            "messages": [],
      +            "prompt_tokens": 123,
      +            "completion_tokens": 45,
      +            "total_tokens": 168,
      +            "reasoning_tokens": 7,
      +            "cache_read_tokens": 11,
      +        })
      +
      +        mock_conn = MagicMock(spec=acp.Client)
      +        mock_conn.session_update = AsyncMock()
      +        agent._conn = mock_conn
      +
      +        prompt = [TextContentBlock(type="text", text="show usage")]
      +        resp = await agent.prompt(prompt=prompt, session_id=new_resp.session_id)
      +
      +        assert isinstance(resp, PromptResponse)
      +        assert resp.usage is not None
      +        assert resp.usage.input_tokens == 123
      +        assert resp.usage.output_tokens == 45
      +        assert resp.usage.total_tokens == 168
      +        assert resp.usage.thought_tokens == 7
      +        assert resp.usage.cached_read_tokens == 11
      +
           @pytest.mark.asyncio
           async def test_prompt_cancelled_returns_cancelled_stop_reason(self, agent):
               """If cancel is called during prompt, stop_reason should be 'cancelled'."""

1 PR closed without merging

From the close comments: merged via another change 1

  • #7047 fix(acp): populate PromptResponse usage from top-level result fields · merged via another change → #7086
Closed issues · 1
  • Closed#7007ACP adapter: PromptResponse.usage is always None — top-level keys missed
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

Merged PRs
#7086
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
10 Apr 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.

For your agent

The same facts, typed.