What happened?
In the hooks reference, the Stable Model API documents LLMResponse.usageMetadata as a single-field object:
docs/hooks/reference.md (line ~328, under **LLMResponse**:):
"usageMetadata": { "totalTokenCount": number }
But the hook-facing translator actually forwards three token fields, not one. packages/core/src/hooks/hookTranslator.ts toHookLLMResponse() (lines ~404-410) builds:
usageMetadata: sdkResponse.usageMetadata
? {
promptTokenCount: sdkResponse.usageMetadata.promptTokenCount,
candidatesTokenCount: sdkResponse.usageMetadata.candidatesTokenCount,
totalTokenCount: sdkResponse.usageMetadata.totalTokenCount,
}
: undefined,
and the exported LLMResponse interface in the same file (lines ~60-63) declares all three as well:
usageMetadata?: {
promptTokenCount?: number;
candidatesTokenCount?: number;
totalTokenCount?: number;
};
So an AfterModel (or BeforeModel) hook receives promptTokenCount and candidatesTokenCount in addition to totalTokenCount, yet the reference — which is explicitly presented as the stable model API hooks can rely on ("Gemini CLI uses these structures to ensure hooks don't break across SDK updates") — lists only totalTokenCount.
What did you expect to happen?
The documented stable schema should match the fields the hook actually receives, i.e.:
"usageMetadata": {
"promptTokenCount": number,
"candidatesTokenCount": number,
"totalTokenCount": number
}
Why this matters
We hit this while building a cross-platform hook/MCP middleware adapter. AfterModel.usageMetadata is the one host-native token-usage signal a hook can read, so we want the exact field list to record accurate per-turn usage. Going by the reference alone, an integrator would assume only totalTokenCount is available and would not read the input/output token breakdown that the code is already handing them. (The per-chunk firing cadence of AfterModel is already correctly noted in docs/hooks/best-practices.md, so this is purely the field list in the schema.)
This is the same omission class — but a different surface — from google-gemini#27985 (ACP PromptResponse usage), which is about the ACP path, not the hooks LLMResponse stable-API schema; cross-referencing for context only.
Proposed fix
A one-block edit to docs/hooks/reference.md: expand the usageMetadata schema under **LLMResponse**: to list promptTokenCount, candidatesTokenCount, and totalTokenCount, matching hookTranslator.ts toHookLLMResponse() and the exported LLMResponse interface.
Happy to open the docs PR if a maintainer confirms this is the intended stable surface (I understand per CONTRIBUTING that the issue comes first).
Client information
Client Information
This is a documentation/code-consistency report based on the current main (docs/hooks/reference.md and packages/core/src/hooks/hookTranslator.ts), not a runtime fault, so a /about capture is not applicable. Platform: N/A (docs).
What happened?
In the hooks reference, the Stable Model API documents
LLMResponse.usageMetadataas a single-field object:docs/hooks/reference.md(line ~328, under**LLMResponse**:):But the hook-facing translator actually forwards three token fields, not one.
packages/core/src/hooks/hookTranslator.tstoHookLLMResponse()(lines ~404-410) builds:and the exported
LLMResponseinterface in the same file (lines ~60-63) declares all three as well:So an
AfterModel(orBeforeModel) hook receivespromptTokenCountandcandidatesTokenCountin addition tototalTokenCount, yet the reference — which is explicitly presented as the stable model API hooks can rely on ("Gemini CLI uses these structures to ensure hooks don't break across SDK updates") — lists onlytotalTokenCount.What did you expect to happen?
The documented stable schema should match the fields the hook actually receives, i.e.:
Why this matters
We hit this while building a cross-platform hook/MCP middleware adapter.
AfterModel.usageMetadatais the one host-native token-usage signal a hook can read, so we want the exact field list to record accurate per-turn usage. Going by the reference alone, an integrator would assume onlytotalTokenCountis available and would not read the input/output token breakdown that the code is already handing them. (The per-chunk firing cadence ofAfterModelis already correctly noted indocs/hooks/best-practices.md, so this is purely the field list in the schema.)This is the same omission class — but a different surface — from google-gemini#27985 (ACP
PromptResponseusage), which is about the ACP path, not the hooksLLMResponsestable-API schema; cross-referencing for context only.Proposed fix
A one-block edit to
docs/hooks/reference.md: expand theusageMetadataschema under**LLMResponse**:to listpromptTokenCount,candidatesTokenCount, andtotalTokenCount, matchinghookTranslator.tstoHookLLMResponse()and the exportedLLMResponseinterface.Happy to open the docs PR if a maintainer confirms this is the intended stable surface (I understand per CONTRIBUTING that the issue comes first).
Client information
Client Information
This is a documentation/code-consistency report based on the current
main(docs/hooks/reference.mdandpackages/core/src/hooks/hookTranslator.ts), not a runtime fault, so a/aboutcapture is not applicable. Platform: N/A (docs).