11import { test , expect , describe } from "bun:test" ;
22import {
33 createCodexResponsesAdapter ,
4+ tagSignature ,
45 CODEX_ACCOUNT_ID_OPTION ,
56 CODEX_SESSION_ID_OPTION ,
7+ CODEX_RESPONSES_PROVIDER ,
68} from "../../src/provider/codex-responses-adapter.js" ;
9+ import { GROK_RESPONSES_PROVIDER } from "../../src/provider/grok-responses-adapter.js" ;
710import { BEARER_CREDENTIAL_SENTINEL } from "@intx/inference" ;
811import type { ConversationTurn , InferenceOptions , LastCycleSource } from "@intx/types/runtime" ;
912
@@ -113,7 +116,7 @@ describe("codex-responses buildRequest", () => {
113116 model : "gpt-5-codex" ,
114117 timestamp : 0 ,
115118 content : [
116- { type : "thinking" , thinking : "internal steps..." , signature : "ENC_BLOB_123" } ,
119+ { type : "thinking" , thinking : "internal steps..." , signature : tagSignature ( CODEX_RESPONSES_PROVIDER , "ENC_BLOB_123" ) } ,
117120 { type : "text" , text : "The answer is 42." } ,
118121 ] ,
119122 } ,
@@ -126,6 +129,28 @@ describe("codex-responses buildRequest", () => {
126129 ] ) ;
127130 } ) ;
128131
132+ test ( "a second account on the same provider still replays the signature" , ( ) => {
133+ // codex/personal and codex/work are two ChatGPT accounts routed through the
134+ // same Codex backend (same provider, different InferenceSource.id). The
135+ // backend can decrypt a signature issued to either account, so a live
136+ // account switch must not poison reasoning continuity.
137+ const turns : ConversationTurn [ ] = [
138+ userTurn ( "solve the hard problem" ) ,
139+ {
140+ role : "assistant" ,
141+ model : "gpt-5-codex" ,
142+ timestamp : 0 ,
143+ content : [
144+ { type : "thinking" , thinking : "internal steps..." , signature : tagSignature ( CODEX_RESPONSES_PROVIDER , "ENC_BLOB_123" ) } ,
145+ { type : "text" , text : "The answer is 42." } ,
146+ ] ,
147+ } ,
148+ ] ;
149+ const workAdapter = createCodexResponsesAdapter ( { sourceId : "codex/work" , provider : "codex-responses" , model : "gpt-5-codex" } ) ;
150+ const body = JSON . parse ( workAdapter . buildRequest ( turns , "gpt-5-codex" , baseOptions ) . body ) as Record < string , unknown > ;
151+ expect ( body [ "input" ] ) . toContainEqual ( { type : "reasoning" , summary : [ ] , encrypted_content : "ENC_BLOB_123" } ) ;
152+ } ) ;
153+
129154 test ( "drops a reasoning signature issued for a different model after a provider switch" , ( ) => {
130155 // The signature was minted by grok-4.5; the request now targets a Codex
131156 // model. Replaying it would 400 with an undecryptable-content error, so
@@ -137,7 +162,31 @@ describe("codex-responses buildRequest", () => {
137162 model : "grok-4.5" ,
138163 timestamp : 0 ,
139164 content : [
140- { type : "thinking" , thinking : "internal steps..." , signature : "FOREIGN_BLOB" } ,
165+ { type : "thinking" , thinking : "internal steps..." , signature : tagSignature ( GROK_RESPONSES_PROVIDER , "FOREIGN_BLOB" ) } ,
166+ { type : "text" , text : "The answer is 42." } ,
167+ ] ,
168+ } ,
169+ ] ;
170+ const body = JSON . parse ( adapter ( ) . buildRequest ( turns , "gpt-5-codex" , baseOptions ) . body ) as Record < string , unknown > ;
171+ expect ( body [ "input" ] ) . toEqual ( [
172+ { type : "message" , role : "user" , content : [ { type : "input_text" , text : "solve the hard problem" } ] } ,
173+ { type : "message" , role : "assistant" , content : [ { type : "output_text" , text : "The answer is 42." } ] } ,
174+ ] ) ;
175+ } ) ;
176+
177+ test ( "drops a reasoning signature issued by a different provider even when the model string matches" , ( ) => {
178+ // Two distinct backends (e.g. proxy aliases) can declare the identical
179+ // literal model name. Nothing but the tagged provider on the signature
180+ // itself distinguishes them, since InferenceSource.model is arbitrary
181+ // catalog text and turn.model alone cannot tell them apart.
182+ const turns : ConversationTurn [ ] = [
183+ userTurn ( "solve the hard problem" ) ,
184+ {
185+ role : "assistant" ,
186+ model : "gpt-5-codex" ,
187+ timestamp : 0 ,
188+ content : [
189+ { type : "thinking" , thinking : "internal steps..." , signature : tagSignature ( GROK_RESPONSES_PROVIDER , "FOREIGN_BLOB" ) } ,
141190 { type : "text" , text : "The answer is 42." } ,
142191 ] ,
143192 } ,
@@ -156,7 +205,10 @@ describe("codex-responses buildRequest", () => {
156205 role : "assistant" ,
157206 model : "grok-4.5" ,
158207 timestamp : 0 ,
159- content : [ { type : "thinking" , thinking : "..." , signature : "FOREIGN_BLOB" } , { type : "text" , text : "ok" } ] ,
208+ content : [
209+ { type : "thinking" , thinking : "..." , signature : tagSignature ( GROK_RESPONSES_PROVIDER , "FOREIGN_BLOB" ) } ,
210+ { type : "text" , text : "ok" } ,
211+ ] ,
160212 } ,
161213 userTurn ( "turn 2" ) ,
162214 ] ;
0 commit comments