@@ -42,6 +42,7 @@ const (
4242 stableSessionResolveRetries = 5
4343 stableSessionResolveDelay = 150 * time .Millisecond
4444 initialSlashCommandsWait = 250 * time .Millisecond
45+ postPromptDrainTimeout = 250 * time .Millisecond
4546)
4647
4748// Config configures one embedded codex runtime provider instance.
@@ -467,14 +468,51 @@ func (c *Client) streamOnce(
467468 promptDone <- promptResult {response : resp , err : reqErr }
468469 }()
469470
471+ var (
472+ finalStopReason agents.StopReason
473+ promptFinished bool
474+ drainTimer * time.Timer
475+ drainCh <- chan time.Time
476+ )
477+ stopDrainTimer := func () {
478+ if drainTimer == nil {
479+ return
480+ }
481+ if ! drainTimer .Stop () {
482+ select {
483+ case <- drainTimer .C :
484+ default :
485+ }
486+ }
487+ drainCh = nil
488+ }
489+ resetDrainTimer := func () {
490+ if drainTimer == nil {
491+ drainTimer = time .NewTimer (postPromptDrainTimeout )
492+ drainCh = drainTimer .C
493+ return
494+ }
495+ if ! drainTimer .Stop () {
496+ select {
497+ case <- drainTimer .C :
498+ default :
499+ }
500+ }
501+ drainTimer .Reset (postPromptDrainTimeout )
502+ drainCh = drainTimer .C
503+ }
504+ defer stopDrainTimer ()
505+
470506 for {
471507 select {
472508 case <- ctx .Done ():
473509 stopCancelWatcher ()
510+ stopDrainTimer ()
474511 return agents .StopReasonCancelled , nil
475512 case result := <- promptDone :
476- stopCancelWatcher ()
477513 if result .err != nil {
514+ stopCancelWatcher ()
515+ stopDrainTimer ()
478516 if errors .Is (result .err , context .Canceled ) || errors .Is (result .err , context .DeadlineExceeded ) || ctx .Err () != nil {
479517 return agents .StopReasonCancelled , nil
480518 }
@@ -483,15 +521,24 @@ func (c *Client) streamOnce(
483521
484522 stopReason , parseErr := parsePromptStopReason (result .response .Result )
485523 if parseErr != nil {
524+ stopCancelWatcher ()
525+ stopDrainTimer ()
486526 return agents .StopReasonEndTurn , parseErr
487527 }
488528 if stopReason == "cancelled" {
489- return agents .StopReasonCancelled , nil
529+ finalStopReason = agents .StopReasonCancelled
530+ } else {
531+ finalStopReason = agents .StopReasonEndTurn
490532 }
491- return agents .StopReasonEndTurn , nil
533+ promptFinished = true
534+ resetDrainTimer ()
492535 case msg , ok := <- updates :
493536 if ! ok {
494537 stopCancelWatcher ()
538+ stopDrainTimer ()
539+ if promptFinished {
540+ return finalStopReason , nil
541+ }
495542 if ctx .Err () != nil {
496543 return agents .StopReasonCancelled , nil
497544 }
@@ -500,8 +547,23 @@ func (c *Client) streamOnce(
500547
501548 if err := c .handleUpdate (ctx , runtime , msg , onDelta ); err != nil {
502549 stopCancelWatcher ()
550+ stopDrainTimer ()
503551 return agents .StopReasonEndTurn , err
504552 }
553+ if promptFinished {
554+ if acpSessionUpdateIsTerminal (msg .Params ) {
555+ stopCancelWatcher ()
556+ stopDrainTimer ()
557+ return finalStopReason , nil
558+ }
559+ resetDrainTimer ()
560+ }
561+ case <- drainCh :
562+ stopCancelWatcher ()
563+ stopDrainTimer ()
564+ if promptFinished {
565+ return finalStopReason , nil
566+ }
505567 }
506568 }
507569}
@@ -548,6 +610,7 @@ func (c *Client) handleUpdate(
548610 observability .LogACPMessage (c .Name (), "inbound" , msg )
549611
550612 if msg .Method == methodSessionUpdate {
613+ updateType := acpSessionUpdateTopLevelType (msg .Params )
551614 update , err := agents .ParseACPUpdate (msg .Params )
552615 if err != nil {
553616 return fmt .Errorf ("codex: %w" , err )
@@ -562,6 +625,15 @@ func (c *Client) handleUpdate(
562625 return err
563626 }
564627 return nil
628+ case agents .ACPUpdateTypeThoughtMessageChunk :
629+ if updateType != "" && updateType != "reasoning" {
630+ return nil
631+ }
632+ if err := agents .NotifyReasoningDelta (ctx , update .Delta ); err != nil {
633+ c .sendSessionCancel (runtime , c .currentSessionID ())
634+ return err
635+ }
636+ return nil
565637 case agents .ACPUpdateTypePlan :
566638 handler , ok := agents .PlanHandlerFromContext (ctx )
567639 if ! ok {
@@ -592,6 +664,41 @@ func (c *Client) handleUpdate(
592664 return nil
593665}
594666
667+ func acpSessionUpdateTopLevelType (raw json.RawMessage ) string {
668+ if len (raw ) == 0 {
669+ return ""
670+ }
671+ var payload struct {
672+ Type string `json:"type"`
673+ }
674+ if err := json .Unmarshal (raw , & payload ); err != nil {
675+ return ""
676+ }
677+ return strings .TrimSpace (payload .Type )
678+ }
679+
680+ func acpSessionUpdateIsTerminal (raw json.RawMessage ) bool {
681+ if len (raw ) == 0 {
682+ return false
683+ }
684+ var payload struct {
685+ Type string `json:"type"`
686+ Status string `json:"status"`
687+ }
688+ if err := json .Unmarshal (raw , & payload ); err != nil {
689+ return false
690+ }
691+ if strings .TrimSpace (payload .Type ) != "status" {
692+ return false
693+ }
694+ switch strings .TrimSpace (payload .Status ) {
695+ case "turn_completed" , "turn_cancelled" :
696+ return true
697+ default :
698+ return false
699+ }
700+ }
701+
595702func (c * Client ) handlePermissionRequest (
596703 ctx context.Context ,
597704 runtime * codexacp.EmbeddedRuntime ,
0 commit comments