@@ -163,6 +163,86 @@ func TestConvertCodexResponseToClaude_StreamThinkingFinalizesPendingBlockBeforeN
163163 }
164164}
165165
166+ func TestConvertCodexResponseToClaude_StreamThinkingRetainsSignatureAcrossMultipartReasoning (t * testing.T ) {
167+ ctx := context .Background ()
168+ originalRequest := []byte (`{"messages":[]}` )
169+ var param any
170+
171+ chunks := [][]byte {
172+ []byte ("data: {\" type\" :\" response.output_item.added\" ,\" item\" :{\" type\" :\" reasoning\" ,\" encrypted_content\" :\" enc_sig_multipart\" }}" ),
173+ []byte ("data: {\" type\" :\" response.reasoning_summary_part.added\" }" ),
174+ []byte ("data: {\" type\" :\" response.reasoning_summary_text.delta\" ,\" delta\" :\" First part\" }" ),
175+ []byte ("data: {\" type\" :\" response.reasoning_summary_part.done\" }" ),
176+ []byte ("data: {\" type\" :\" response.reasoning_summary_part.added\" }" ),
177+ []byte ("data: {\" type\" :\" response.reasoning_summary_text.delta\" ,\" delta\" :\" Second part\" }" ),
178+ []byte ("data: {\" type\" :\" response.reasoning_summary_part.done\" }" ),
179+ []byte ("data: {\" type\" :\" response.output_item.done\" ,\" item\" :{\" type\" :\" reasoning\" }}" ),
180+ }
181+
182+ var outputs [][]byte
183+ for _ , chunk := range chunks {
184+ outputs = append (outputs , ConvertCodexResponseToClaude (ctx , "" , originalRequest , nil , chunk , & param )... )
185+ }
186+
187+ signatureDeltaCount := 0
188+ for _ , out := range outputs {
189+ for _ , line := range strings .Split (string (out ), "\n " ) {
190+ if ! strings .HasPrefix (line , "data: " ) {
191+ continue
192+ }
193+ data := gjson .Parse (strings .TrimPrefix (line , "data: " ))
194+ if data .Get ("type" ).String () == "content_block_delta" && data .Get ("delta.type" ).String () == "signature_delta" {
195+ signatureDeltaCount ++
196+ if got := data .Get ("delta.signature" ).String (); got != "enc_sig_multipart" {
197+ t .Fatalf ("unexpected signature delta: %q" , got )
198+ }
199+ }
200+ }
201+ }
202+
203+ if signatureDeltaCount != 2 {
204+ t .Fatalf ("expected signature_delta for both multipart thinking blocks, got %d" , signatureDeltaCount )
205+ }
206+ }
207+
208+ func TestConvertCodexResponseToClaude_StreamThinkingUsesEarlyCapturedSignatureWhenDoneOmitsIt (t * testing.T ) {
209+ ctx := context .Background ()
210+ originalRequest := []byte (`{"messages":[]}` )
211+ var param any
212+
213+ chunks := [][]byte {
214+ []byte ("data: {\" type\" :\" response.output_item.added\" ,\" item\" :{\" type\" :\" reasoning\" ,\" encrypted_content\" :\" enc_sig_early\" }}" ),
215+ []byte ("data: {\" type\" :\" response.reasoning_summary_part.added\" }" ),
216+ []byte ("data: {\" type\" :\" response.reasoning_summary_text.delta\" ,\" delta\" :\" Let me think\" }" ),
217+ []byte ("data: {\" type\" :\" response.output_item.done\" ,\" item\" :{\" type\" :\" reasoning\" }}" ),
218+ }
219+
220+ var outputs [][]byte
221+ for _ , chunk := range chunks {
222+ outputs = append (outputs , ConvertCodexResponseToClaude (ctx , "" , originalRequest , nil , chunk , & param )... )
223+ }
224+
225+ signatureDeltaCount := 0
226+ for _ , out := range outputs {
227+ for _ , line := range strings .Split (string (out ), "\n " ) {
228+ if ! strings .HasPrefix (line , "data: " ) {
229+ continue
230+ }
231+ data := gjson .Parse (strings .TrimPrefix (line , "data: " ))
232+ if data .Get ("type" ).String () == "content_block_delta" && data .Get ("delta.type" ).String () == "signature_delta" {
233+ signatureDeltaCount ++
234+ if got := data .Get ("delta.signature" ).String (); got != "enc_sig_early" {
235+ t .Fatalf ("unexpected signature delta: %q" , got )
236+ }
237+ }
238+ }
239+ }
240+
241+ if signatureDeltaCount != 1 {
242+ t .Fatalf ("expected signature_delta from early-captured signature, got %d" , signatureDeltaCount )
243+ }
244+ }
245+
166246func TestConvertCodexResponseToClaudeNonStream_ThinkingIncludesSignature (t * testing.T ) {
167247 ctx := context .Background ()
168248 originalRequest := []byte (`{"messages":[]}` )
0 commit comments