diff --git a/internal/runtime/executor/codex_websockets_execute.go b/internal/runtime/executor/codex_websockets_execute.go index f8c51ef0..fb1fb719 100644 --- a/internal/runtime/executor/codex_websockets_execute.go +++ b/internal/runtime/executor/codex_websockets_execute.go @@ -251,8 +251,8 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut } } - if optimizeMultiAgentV2 { - sess.markMultiAgentV2Optimized(conn) + if optimizeMultiAgentV2 || multiAgentV2Conflict { + sess.setMultiAgentV2Optimized(conn, optimizeMultiAgentV2 && !multiAgentV2Conflict) } outputItemsByIndex := make(map[int64][]byte) diff --git a/internal/runtime/executor/codex_websockets_session.go b/internal/runtime/executor/codex_websockets_session.go index 9c721ca7..10219fc3 100644 --- a/internal/runtime/executor/codex_websockets_session.go +++ b/internal/runtime/executor/codex_websockets_session.go @@ -164,13 +164,17 @@ func (s *codexWebsocketSession) writeMessage(conn *websocket.Conn, msgType int, return conn.WriteMessage(msgType, payload) } -func (s *codexWebsocketSession) markMultiAgentV2Optimized(conn *websocket.Conn) { +func (s *codexWebsocketSession) setMultiAgentV2Optimized(conn *websocket.Conn, optimized bool) { if s == nil || conn == nil { return } s.connMu.Lock() if s.conn == conn { - s.multiAgentV2OptimizedConn = conn + if optimized { + s.multiAgentV2OptimizedConn = conn + } else { + s.multiAgentV2OptimizedConn = nil + } } s.connMu.Unlock() } diff --git a/internal/runtime/executor/codex_websockets_spawn_agent_test.go b/internal/runtime/executor/codex_websockets_spawn_agent_test.go index fc3771b6..6b3faacd 100644 --- a/internal/runtime/executor/codex_websockets_spawn_agent_test.go +++ b/internal/runtime/executor/codex_websockets_spawn_agent_test.go @@ -27,7 +27,7 @@ func TestCodexWebsocketsExecutorRestoresMultiAgentV2NamespaceAcrossIncrementalTu } { t.Run(tt.name, func(t *testing.T) { upgrader := websocket.Upgrader{CheckOrigin: func(*http.Request) bool { return true }} - capturedPayload := make(chan []byte, 3) + capturedPayload := make(chan []byte, 6) var connectionCount atomic.Int32 var requestCount atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { @@ -51,7 +51,7 @@ func TestCodexWebsocketsExecutorRestoresMultiAgentV2NamespaceAcrossIncrementalTu t.Errorf("write websocket response: %v", errWrite) return } - if turn == 3 { + if turn == 6 { return } } @@ -126,6 +126,31 @@ func TestCodexWebsocketsExecutorRestoresMultiAgentV2NamespaceAcrossIncrementalTu t.Fatalf("user-defined collaboration-optimize namespace was rewritten: %s", conflictingClientPayload) } + fourthRequest := []byte(`{"model":"gpt-5.4","previous_response_id":"resp_3","input":[{"type":"function_call_output","call_id":"call_3","output":"done"}]}`) + fourthClientPayload := execute(fourthRequest) + fourthUpstreamPayload := <-capturedPayload + if strings.Contains(string(fourthUpstreamPayload), "collaboration") || strings.Contains(string(fourthUpstreamPayload), "spawn_agent") { + t.Fatalf("post-conflict incremental upstream request unexpectedly contains collaboration tools: %s", fourthUpstreamPayload) + } + if !strings.Contains(string(fourthClientPayload), `"namespace":"collaboration-optimize"`) { + t.Fatalf("user-defined namespace was rewritten on the post-conflict incremental turn: %s", fourthClientPayload) + } + + fifthClientPayload := execute(codexSpawnAgentTestPayload()) + fifthUpstreamPayload := <-capturedPayload + if namespace := gjson.GetBytes(fifthUpstreamPayload, "input.0.tools.0.name").String(); namespace != "collaboration-optimize" { + t.Fatalf("re-enabled upstream namespace = %q, want collaboration-optimize", namespace) + } + assertCodexSpawnAgentClientNamespace(t, fifthClientPayload) + + sixthRequest := []byte(`{"model":"gpt-5.4","previous_response_id":"resp_5","input":[{"type":"function_call_output","call_id":"call_5","output":"done"}]}`) + sixthClientPayload := execute(sixthRequest) + sixthUpstreamPayload := <-capturedPayload + if strings.Contains(string(sixthUpstreamPayload), "collaboration") || strings.Contains(string(sixthUpstreamPayload), "spawn_agent") { + t.Fatalf("re-enabled incremental upstream request unexpectedly contains collaboration tools: %s", sixthUpstreamPayload) + } + assertCodexSpawnAgentClientNamespace(t, sixthClientPayload) + if got := connectionCount.Load(); got != 1 { t.Fatalf("upstream websocket connections = %d, want 1", got) } diff --git a/internal/runtime/executor/codex_websockets_stream.go b/internal/runtime/executor/codex_websockets_stream.go index 84ea8495..1cad7676 100644 --- a/internal/runtime/executor/codex_websockets_stream.go +++ b/internal/runtime/executor/codex_websockets_stream.go @@ -258,8 +258,8 @@ func (e *CodexWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *clipr } } - if optimizeMultiAgentV2 { - sess.markMultiAgentV2Optimized(conn) + if optimizeMultiAgentV2 || multiAgentV2Conflict { + sess.setMultiAgentV2Optimized(conn, optimizeMultiAgentV2 && !multiAgentV2Conflict) } out := make(chan cliproxyexecutor.StreamChunk)