@@ -394,24 +394,41 @@ function createCodexFetch(
394394 try {
395395 const originalBody = JSON . parse ( init . body )
396396
397+ // Extract system message for instructions and separate non-system messages
398+ const allMessages = originalBody . messages || [ ]
399+ const systemMessage = allMessages . find (
400+ ( m : { role : string } ) => m . role === 'system' ,
401+ )
402+ const nonSystemMessages = allMessages . filter (
403+ ( m : { role : string } ) => m . role !== 'system' ,
404+ )
405+
406+ // Extract instructions from system message (Codex API REQUIRES this field)
407+ let instructions = 'You are a helpful assistant.'
408+ if ( systemMessage ) {
409+ instructions =
410+ typeof systemMessage . content === 'string'
411+ ? systemMessage . content
412+ : systemMessage . content
413+ ?. map ( ( p : { text ?: string } ) => p . text )
414+ . filter ( Boolean )
415+ . join ( '\n' ) || 'You are a helpful assistant.'
416+ }
417+
397418 // Transform from OpenAI chat format to Codex format
398419 const codexBody : Record < string , unknown > = {
399420 model : modelId ,
400- // Transform messages to Codex input format
401- input : transformMessagesToCodexInput ( originalBody . messages || [ ] ) ,
402- // Codex-specific required fields
421+ instructions,
422+ input : transformMessagesToCodexInput ( nonSystemMessages ) ,
403423 store : false , // ChatGPT backend REQUIRES store=false
404424 stream : true , // Always stream
405- // Reasoning configuration
406425 reasoning : {
407- effort : 'medium ' ,
426+ effort : 'high ' ,
408427 summary : 'auto' ,
409428 } ,
410- // Text verbosity
411429 text : {
412430 verbosity : 'medium' ,
413431 } ,
414- // Include reasoning in response
415432 include : [ 'reasoning.encrypted_content' ] ,
416433 }
417434
@@ -428,38 +445,13 @@ function createCodexFetch(
428445 name : fn . name ,
429446 description : fn . description ,
430447 parameters : fn . parameters ,
431- // Preserve any additional properties
432448 ...( fn . strict !== undefined && { strict : fn . strict } ) ,
433449 }
434450 }
435- // Already in Codex format or unknown format, pass through
436451 return tool
437452 } )
438453 }
439454
440- // Extract system message for instructions (required by Codex API)
441- const systemMessage = ( originalBody . messages || [ ] ) . find (
442- ( m : { role : string } ) => m . role === 'system' ,
443- )
444- if ( systemMessage ) {
445- codexBody . instructions =
446- typeof systemMessage . content === 'string'
447- ? systemMessage . content
448- : systemMessage . content
449- ?. map ( ( p : { text ?: string } ) => p . text )
450- . filter ( Boolean )
451- . join ( '\n' ) || 'You are a helpful assistant.'
452- // Remove system message from input (it's now in instructions)
453- codexBody . input = transformMessagesToCodexInput (
454- ( originalBody . messages || [ ] ) . filter (
455- ( m : { role : string } ) => m . role !== 'system' ,
456- ) ,
457- )
458- } else {
459- // Codex API REQUIRES instructions field - provide default if no system message
460- codexBody . instructions = 'You are a helpful assistant.'
461- }
462-
463455 transformedBody = JSON . stringify ( codexBody )
464456 } catch {
465457 // If parsing fails, use original body
@@ -471,6 +463,7 @@ function createCodexFetch(
471463 `${ CHATGPT_BACKEND_API_URL } /codex/responses` ,
472464 {
473465 method : 'POST' ,
466+ signal : init ?. signal ,
474467 headers : {
475468 'Content-Type' : 'application/json' ,
476469 Authorization : `Bearer ${ oauthToken } ` ,
@@ -510,10 +503,10 @@ function createCodexFetch(
510503
511504 for ( const line of lines ) {
512505 const trimmed = line . trim ( )
513- if ( ! trimmed ) continue
506+ // Only parse SSE data: lines, skip event:/id:/retry: and blank lines
507+ if ( ! trimmed . startsWith ( 'data:' ) ) continue
514508
515- // Parse the SSE data line
516- const data = trimmed . startsWith ( 'data: ' ) ? trimmed . slice ( 6 ) : trimmed
509+ const data = trimmed . slice ( 5 ) . trim ( )
517510 if ( data === '[DONE]' ) continue
518511
519512 try {
@@ -533,9 +526,9 @@ function createCodexFetch(
533526 const lines = buffer . split ( '\n' )
534527 for ( const line of lines ) {
535528 const trimmed = line . trim ( )
536- if ( ! trimmed ) continue
529+ if ( ! trimmed . startsWith ( 'data:' ) ) continue
537530
538- const data = trimmed . startsWith ( 'data: ' ) ? trimmed . slice ( 6 ) : trimmed
531+ const data = trimmed . slice ( 5 ) . trim ( )
539532 if ( data === '[DONE]' ) continue
540533
541534 try {
0 commit comments