@@ -8,6 +8,8 @@ use crate::fragment::AGENTS_MD_START_MARKER;
88use crate :: fragment:: SKILL_FRAGMENT ;
99
1010pub const USER_INSTRUCTIONS_PREFIX : & str = AGENTS_MD_START_MARKER ;
11+ const INSTRUCTIONS_CLOSE_TAG : & str = "</INSTRUCTIONS>" ;
12+ const ESCAPED_INSTRUCTIONS_CLOSE_TAG : & str = "<\\ /INSTRUCTIONS>" ;
1113
1214#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq ) ]
1315#[ serde( rename = "user_instructions" , rename_all = "snake_case" ) ]
@@ -18,16 +20,45 @@ pub struct UserInstructions {
1820
1921impl UserInstructions {
2022 pub fn serialize_to_text ( & self ) -> String {
23+ let contents = escape_reserved_instruction_delimiters ( & self . text ) ;
2124 format ! (
2225 "{prefix}{directory}\n \n <INSTRUCTIONS>\n {contents}\n {suffix}" ,
2326 prefix = AGENTS_MD_FRAGMENT . start_marker( ) ,
2427 directory = self . directory,
25- contents = self . text ,
28+ contents = contents ,
2629 suffix = AGENTS_MD_FRAGMENT . end_marker( ) ,
2730 )
2831 }
2932}
3033
34+ fn escape_reserved_instruction_delimiters ( text : & str ) -> String {
35+ let Some ( index) = find_ascii_case_insensitive ( text, INSTRUCTIONS_CLOSE_TAG ) else {
36+ return text. to_string ( ) ;
37+ } ;
38+
39+ let mut output = String :: with_capacity ( text. len ( ) ) ;
40+ let mut remaining = text;
41+ let mut next_index = index;
42+ loop {
43+ output. push_str ( & remaining[ ..next_index] ) ;
44+ output. push_str ( ESCAPED_INSTRUCTIONS_CLOSE_TAG ) ;
45+ remaining = & remaining[ next_index + INSTRUCTIONS_CLOSE_TAG . len ( ) ..] ;
46+
47+ let Some ( index) = find_ascii_case_insensitive ( remaining, INSTRUCTIONS_CLOSE_TAG ) else {
48+ output. push_str ( remaining) ;
49+ return output;
50+ } ;
51+ next_index = index;
52+ }
53+ }
54+
55+ fn find_ascii_case_insensitive ( haystack : & str , needle : & str ) -> Option < usize > {
56+ haystack
57+ . as_bytes ( )
58+ . windows ( needle. len ( ) )
59+ . position ( |window| window. eq_ignore_ascii_case ( needle. as_bytes ( ) ) )
60+ }
61+
3162impl From < UserInstructions > for ResponseItem {
3263 fn from ( ui : UserInstructions ) -> Self {
3364 AGENTS_MD_FRAGMENT . into_message ( ui. serialize_to_text ( ) )
0 commit comments