@@ -15,6 +15,27 @@ use std::pin::Pin;
1515/// Claude Code identification text for OAuth requests
1616const CLAUDE_CODE_SPOOF_TEXT : & str = "You are Claude Code, Anthropic's official CLI for Claude." ;
1717
18+ /// Extract system messages from conversation, concatenating multiple with double newlines.
19+ /// Returns None if no system messages present.
20+ fn extract_system_content ( messages : & [ ( String , String ) ] ) -> Option < String > {
21+ let system_messages: Vec < & str > = messages
22+ . iter ( )
23+ . filter_map ( |( role, content) | {
24+ if role == "system" {
25+ Some ( content. as_str ( ) )
26+ } else {
27+ None
28+ }
29+ } )
30+ . collect ( ) ;
31+
32+ if system_messages. is_empty ( ) {
33+ None
34+ } else {
35+ Some ( system_messages. join ( "\n \n " ) )
36+ }
37+ }
38+
1839/// Prepend Claude Code system block to an existing system prompt (for OAuth requests)
1940fn prepend_claude_code_system ( existing : Option < SystemContent > ) -> SystemContent {
2041 let spoof_block = SystemBlock :: text_with_cache ( CLAUDE_CODE_SPOOF_TEXT ) ;
@@ -305,12 +326,16 @@ impl CliClient {
305326 config,
306327 is_oauth,
307328 } => {
329+ // Extract system messages first (they go in a top-level field, not in messages)
330+ let user_system = extract_system_content ( & messages) ;
331+
332+ // Filter to only user/assistant messages
308333 let msgs: Vec < AnthropicMessage > = messages
309334 . iter ( )
310335 . filter_map ( |( role, content) | match role. as_str ( ) {
311336 "user" => Some ( AnthropicMessage :: user ( content. as_str ( ) ) ) ,
312337 "assistant" => Some ( AnthropicMessage :: assistant ( content. as_str ( ) ) ) ,
313- _ => None , // Skip system messages for now
338+ _ => None ,
314339 } )
315340 . collect ( ) ;
316341
@@ -322,8 +347,27 @@ impl CliClient {
322347 builder = builder. temperature ( temp) ;
323348 }
324349
325- if * is_oauth {
326- builder = builder. system_blocks ( prepend_claude_code_system ( None ) . into_blocks ( ) ) ;
350+ // Attach system content (combining with OAuth prefix if needed)
351+ let system_content = match ( user_system, * is_oauth) {
352+ ( Some ( text) , true ) => {
353+ // OAuth + user system: prepend Claude Code to user's system
354+ Some ( prepend_claude_code_system ( Some ( SystemContent :: Text (
355+ text. into ( ) ,
356+ ) ) ) )
357+ }
358+ ( Some ( text) , false ) => {
359+ // No OAuth + user system: just user's system
360+ Some ( SystemContent :: Text ( text. into ( ) ) )
361+ }
362+ ( None , true ) => {
363+ // OAuth + no user system: just Claude Code
364+ Some ( prepend_claude_code_system ( None ) )
365+ }
366+ ( None , false ) => None ,
367+ } ;
368+
369+ if let Some ( content) = system_content {
370+ builder = builder. system_blocks ( content. into_blocks ( ) ) ;
327371 }
328372
329373 let request = builder. build ( ) ;
0 commit comments