@@ -55,11 +55,18 @@ impl ChatComposerHistory {
5555 /// Record a message submitted by the user in the current session so it can
5656 /// be recalled later.
5757 pub fn record_local_submission ( & mut self , text : & str ) {
58- if !text. is_empty ( ) {
59- self . local_history . push ( text. to_string ( ) ) ;
60- self . history_cursor = None ;
61- self . last_history_text = None ;
58+ if text. is_empty ( ) {
59+ return ;
60+ }
61+
62+ // Avoid inserting a duplicate if identical to the previous entry.
63+ if self . local_history . last ( ) . is_some_and ( |prev| prev == text) {
64+ return ;
6265 }
66+
67+ self . local_history . push ( text. to_string ( ) ) ;
68+ self . history_cursor = None ;
69+ self . last_history_text = None ;
6370 }
6471
6572 /// Should Up/Down key presses be interpreted as history navigation given
@@ -187,6 +194,29 @@ mod tests {
187194 use codex_core:: protocol:: Op ;
188195 use std:: sync:: mpsc:: channel;
189196
197+ #[ test]
198+ fn duplicate_submissions_are_not_recorded ( ) {
199+ let mut history = ChatComposerHistory :: new ( ) ;
200+
201+ // Empty submissions are ignored.
202+ history. record_local_submission ( "" ) ;
203+ assert_eq ! ( history. local_history. len( ) , 0 ) ;
204+
205+ // First entry is recorded.
206+ history. record_local_submission ( "hello" ) ;
207+ assert_eq ! ( history. local_history. len( ) , 1 ) ;
208+ assert_eq ! ( history. local_history. last( ) . unwrap( ) , "hello" ) ;
209+
210+ // Identical consecutive entry is skipped.
211+ history. record_local_submission ( "hello" ) ;
212+ assert_eq ! ( history. local_history. len( ) , 1 ) ;
213+
214+ // Different entry is recorded.
215+ history. record_local_submission ( "world" ) ;
216+ assert_eq ! ( history. local_history. len( ) , 2 ) ;
217+ assert_eq ! ( history. local_history. last( ) . unwrap( ) , "world" ) ;
218+ }
219+
190220 #[ test]
191221 fn navigation_with_async_fetch ( ) {
192222 let ( tx, rx) = channel :: < AppEvent > ( ) ;
0 commit comments