@@ -149,12 +149,13 @@ describe("codex-sse fixtures (golden parse)", () => {
149149 ) . toBe ( true ) ;
150150 } ) ;
151151
152- test ( "incomplete stream emits partial text and is terminal without usage " , ( ) => {
152+ test ( "incomplete stream emits partial text and usage and is terminal " , ( ) => {
153153 const out = parseFixture ( "incomplete.json" ) ;
154154
155155 expect ( eventTypes ( out ) ) . toEqual ( [
156156 "inference.text.delta" ,
157157 "inference.text.delta" ,
158+ "inference.usage" ,
158159 ] ) ;
159160 expect ( out [ 0 ] ) . toMatchObject ( {
160161 type : "inference.text.delta" ,
@@ -164,8 +165,22 @@ describe("codex-sse fixtures (golden parse)", () => {
164165 type : "inference.text.delta" ,
165166 data : { token : "answer" , index : 0 } ,
166167 } ) ;
167- // response.incomplete is intentionally not mapped to usage (completed-only).
168- expect ( out . some ( ( e ) => e . type === "inference.usage" ) ) . toBe ( false ) ;
168+ // A truncated turn is still billed: the backend reports usage for the
169+ // partial turn (input plus output emitted before the cap), so
170+ // response.incomplete maps to usage like response.completed does.
171+ expect ( out [ 2 ] ) . toMatchObject ( {
172+ type : "inference.usage" ,
173+ data : {
174+ usage : {
175+ input : 50 ,
176+ output : 8 ,
177+ cacheRead : 0 ,
178+ cacheWrite : 0 ,
179+ thinking : 0 ,
180+ } ,
181+ source : SOURCE ,
182+ } ,
183+ } ) ;
169184
170185 const lastPayload = defined (
171186 loadFixture ( "incomplete.json" ) . at ( - 1 ) ,
0 commit comments