diff --git a/internal/logging/request_logger.go b/internal/logging/request_logger.go index faa81df7..2db2a504 100644 --- a/internal/logging/request_logger.go +++ b/internal/logging/request_logger.go @@ -570,6 +570,9 @@ func (l *FileRequestLogger) writeNonStreamingLog( return errWrite } if isWebsocketTranscript { + // Intentionally omit the generic downstream HTTP response section for websocket + // transcripts. The durable session exchange is captured in WEBSOCKET TIMELINE, + // and appending a one-off upgrade response snapshot would dilute that transcript. return nil } return writeResponseSection(w, statusCode, true, responseHeaders, bytes.NewReader(response), decompressErr, true) @@ -949,6 +952,8 @@ func (l *FileRequestLogger) formatLogContent(url, method string, headers map[str } if isWebsocketTranscript { + // Mirror writeNonStreamingLog: websocket transcripts end with the dedicated + // timeline sections instead of a generic downstream HTTP response block. return content.String() } diff --git a/internal/runtime/executor/codex_websockets_executor.go b/internal/runtime/executor/codex_websockets_executor.go index 363f3ea8..2041cebc 100644 --- a/internal/runtime/executor/codex_websockets_executor.go +++ b/internal/runtime/executor/codex_websockets_executor.go @@ -247,10 +247,7 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut helps.RecordAPIWebsocketError(ctx, e.cfg, "dial", errDial) return resp, errDial } - if respHS != nil { - helps.RecordAPIWebsocketHandshake(ctx, e.cfg, respHS.StatusCode, respHS.Header.Clone()) - } - closeHTTPResponseBody(respHS, "codex websockets executor: close handshake response body error") + recordAPIWebsocketHandshake(ctx, e.cfg, respHS) if sess == nil { logCodexWebsocketConnected(executionSessionID, authID, wsURL) defer func() { @@ -279,7 +276,7 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut // Retry once with a fresh websocket connection. This is mainly to handle // upstream closing the socket between sequential requests within the same // execution session. - connRetry, _, errDialRetry := e.ensureUpstreamConn(ctx, auth, sess, authID, wsURL, wsHeaders) + connRetry, respHSRetry, errDialRetry := e.ensureUpstreamConn(ctx, auth, sess, authID, wsURL, wsHeaders) if errDialRetry == nil && connRetry != nil { wsReqBodyRetry := buildCodexWebsocketRequestBody(body) helps.RecordAPIWebsocketRequest(ctx, e.cfg, helps.UpstreamRequestLog{ @@ -293,6 +290,7 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut AuthType: authType, AuthValue: authValue, }) + recordAPIWebsocketHandshake(ctx, e.cfg, respHSRetry) if errSendRetry := writeCodexWebsocketMessage(sess, connRetry, wsReqBodyRetry); errSendRetry == nil { conn = connRetry wsReqBody = wsReqBodyRetry @@ -302,6 +300,7 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut return resp, errSendRetry } } else { + closeHTTPResponseBody(respHSRetry, "codex websockets executor: close handshake response body error") helps.RecordAPIWebsocketError(ctx, e.cfg, "dial_retry", errDialRetry) return resp, errDialRetry } @@ -449,10 +448,7 @@ func (e *CodexWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *clipr } return nil, errDial } - if respHS != nil { - helps.RecordAPIWebsocketHandshake(ctx, e.cfg, respHS.StatusCode, respHS.Header.Clone()) - } - closeHTTPResponseBody(respHS, "codex websockets executor: close handshake response body error") + recordAPIWebsocketHandshake(ctx, e.cfg, respHS) if sess == nil { logCodexWebsocketConnected(executionSessionID, authID, wsURL) @@ -470,8 +466,9 @@ func (e *CodexWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *clipr e.invalidateUpstreamConn(sess, conn, "send_error", errSend) // Retry once with a new websocket connection for the same execution session. - connRetry, _, errDialRetry := e.ensureUpstreamConn(ctx, auth, sess, authID, wsURL, wsHeaders) + connRetry, respHSRetry, errDialRetry := e.ensureUpstreamConn(ctx, auth, sess, authID, wsURL, wsHeaders) if errDialRetry != nil || connRetry == nil { + closeHTTPResponseBody(respHSRetry, "codex websockets executor: close handshake response body error") helps.RecordAPIWebsocketError(ctx, e.cfg, "dial_retry", errDialRetry) sess.clearActive(readCh) sess.reqMu.Unlock() @@ -489,6 +486,7 @@ func (e *CodexWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *clipr AuthType: authType, AuthValue: authValue, }) + recordAPIWebsocketHandshake(ctx, e.cfg, respHSRetry) if errSendRetry := writeCodexWebsocketMessage(sess, connRetry, wsReqBodyRetry); errSendRetry != nil { helps.RecordAPIWebsocketError(ctx, e.cfg, "send_retry", errSendRetry) e.invalidateUpstreamConn(sess, connRetry, "send_error", errSendRetry) @@ -1044,6 +1042,14 @@ func websocketUpgradeRequestLog(info helps.UpstreamRequestLog) helps.UpstreamReq return upgradeInfo } +func recordAPIWebsocketHandshake(ctx context.Context, cfg *config.Config, resp *http.Response) { + if resp == nil { + return + } + helps.RecordAPIWebsocketHandshake(ctx, cfg, resp.StatusCode, resp.Header.Clone()) + closeHTTPResponseBody(resp, "codex websockets executor: close handshake response body error") +} + func websocketHandshakeBody(resp *http.Response) []byte { if resp == nil || resp.Body == nil { return nil