@@ -35,6 +35,9 @@ pub struct ToolCallRecord {
3535 pub tool_name : String ,
3636 pub args_summary : String ,
3737 pub result_summary : String ,
38+ /// Tool call ID for proper message pairing (optional for backwards compat)
39+ #[ serde( default ) ]
40+ pub tool_id : Option < String > ,
3841}
3942
4043/// Conversation history manager with compaction support
@@ -163,36 +166,97 @@ impl ConversationHistory {
163166 }
164167
165168 /// Create a text summary of conversation turns
169+ /// Includes detailed tool call information to preserve context
166170 fn create_summary ( & self , turns : & [ ConversationTurn ] ) -> String {
171+ use std:: collections:: HashSet ;
172+
167173 let mut summary_parts = Vec :: new ( ) ;
174+ let mut all_files_read: HashSet < String > = HashSet :: new ( ) ;
175+ let mut all_files_written: HashSet < String > = HashSet :: new ( ) ;
168176
169177 for ( i, turn) in turns. iter ( ) . enumerate ( ) {
170178 let mut turn_summary = format ! (
171- "Turn {}: User asked about : {}" ,
179+ "Turn {}: User: {}" ,
172180 i + 1 ,
173- Self :: truncate_text( & turn. user_message, 100 )
181+ Self :: truncate_text( & turn. user_message, 150 )
174182 ) ;
175183
176184 if !turn. tool_calls . is_empty ( ) {
177- let tool_names: Vec < _ > = turn. tool_calls . iter ( )
178- . map ( |tc| tc. tool_name . as_str ( ) )
179- . collect ( ) ;
180- turn_summary. push_str ( & format ! ( ". Tools used: {}" , tool_names. join( ", " ) ) ) ;
185+ // Group tool calls by type for better summary
186+ let mut reads = Vec :: new ( ) ;
187+ let mut writes = Vec :: new ( ) ;
188+ let mut other = Vec :: new ( ) ;
189+
190+ for tc in & turn. tool_calls {
191+ match tc. tool_name . as_str ( ) {
192+ "read_file" => {
193+ reads. push ( tc. args_summary . clone ( ) ) ;
194+ all_files_read. insert ( tc. args_summary . clone ( ) ) ;
195+ }
196+ "write_file" | "write_files" => {
197+ writes. push ( tc. args_summary . clone ( ) ) ;
198+ all_files_written. insert ( tc. args_summary . clone ( ) ) ;
199+ }
200+ "list_directory" => {
201+ other. push ( format ! ( "listed {}" , tc. args_summary) ) ;
202+ }
203+ _ => {
204+ other. push ( format ! ( "{}({})" , tc. tool_name, Self :: truncate_text( & tc. args_summary, 30 ) ) ) ;
205+ }
206+ }
207+ }
208+
209+ if !reads. is_empty ( ) {
210+ turn_summary. push_str ( & format ! ( "\n - Read {} files: {}" ,
211+ reads. len( ) ,
212+ reads. iter( ) . take( 5 ) . cloned( ) . collect:: <Vec <_>>( ) . join( ", " )
213+ ) ) ;
214+ if reads. len ( ) > 5 {
215+ turn_summary. push_str ( & format ! ( " (+{} more)" , reads. len( ) - 5 ) ) ;
216+ }
217+ }
218+ if !writes. is_empty ( ) {
219+ turn_summary. push_str ( & format ! ( "\n - Wrote: {}" , writes. join( ", " ) ) ) ;
220+ }
221+ if !other. is_empty ( ) {
222+ turn_summary. push_str ( & format ! ( "\n - Other: {}" , other. join( ", " ) ) ) ;
223+ }
181224 }
182225
183226 turn_summary. push_str ( & format ! (
184- ". Response summary : {}" ,
185- Self :: truncate_text( & turn. assistant_response, 200 )
227+ "\n - Response : {}" ,
228+ Self :: truncate_text( & turn. assistant_response, 300 )
186229 ) ) ;
187230
188231 summary_parts. push ( turn_summary) ;
189232 }
190233
191- format ! (
192- "Previous conversation summary ({} turns compressed):\n {}" ,
234+ // Add a cumulative context section
235+ let mut context = format ! (
236+ "=== Conversation Summary ({} turns compressed) ===\n \n {}" ,
193237 turns. len( ) ,
194- summary_parts. join( "\n " )
195- )
238+ summary_parts. join( "\n \n " )
239+ ) ;
240+
241+ // Add cumulative file context
242+ if !all_files_read. is_empty ( ) {
243+ context. push_str ( "\n \n === Files Previously Read (content is in context) ===\n " ) ;
244+ for file in all_files_read. iter ( ) . take ( 30 ) {
245+ context. push_str ( & format ! ( " - {}\n " , file) ) ;
246+ }
247+ if all_files_read. len ( ) > 30 {
248+ context. push_str ( & format ! ( " ... and {} more files\n " , all_files_read. len( ) - 30 ) ) ;
249+ }
250+ }
251+
252+ if !all_files_written. is_empty ( ) {
253+ context. push_str ( "\n === Files Previously Written ===\n " ) ;
254+ for file in & all_files_written {
255+ context. push_str ( & format ! ( " - {}\n " , file) ) ;
256+ }
257+ }
258+
259+ context
196260 }
197261
198262 /// Truncate text with ellipsis
@@ -205,6 +269,8 @@ impl ConversationHistory {
205269 }
206270
207271 /// Convert history to Rig Message format for the agent
272+ /// Uses text summaries to preserve context without breaking Rig's internal tool call tracking
273+ /// Note: We can't use proper ToolCall/ToolResult messages because Rig expects real API tool IDs
208274 pub fn to_messages ( & self ) -> Vec < Message > {
209275 use rig:: completion:: message:: { Text , UserContent , AssistantContent } ;
210276 use rig:: OneOrMany ;
@@ -227,7 +293,7 @@ impl ConversationHistory {
227293 } ) ;
228294 }
229295
230- // Add recent turns
296+ // Add recent turns with tool call context as text (not as actual ToolCall messages)
231297 for turn in & self . turns {
232298 // User message
233299 messages. push ( Message :: User {
@@ -236,12 +302,30 @@ impl ConversationHistory {
236302 } ) ) ,
237303 } ) ;
238304
239- // Assistant response (simplified - just the text response)
240- // Note: Tool calls are implicitly part of the response context
305+ // Build assistant response that includes tool call context
306+ let mut response_text = String :: new ( ) ;
307+
308+ // If there were tool calls, include them as text context
309+ if !turn. tool_calls . is_empty ( ) {
310+ response_text. push_str ( "[Tools used in this turn:\n " ) ;
311+ for tc in & turn. tool_calls {
312+ response_text. push_str ( & format ! (
313+ " - {}({}) → {}\n " ,
314+ tc. tool_name,
315+ Self :: truncate_text( & tc. args_summary, 50 ) ,
316+ Self :: truncate_text( & tc. result_summary, 100 )
317+ ) ) ;
318+ }
319+ response_text. push_str ( "]\n \n " ) ;
320+ }
321+
322+ // Add the actual response
323+ response_text. push_str ( & turn. assistant_response ) ;
324+
241325 messages. push ( Message :: Assistant {
242326 id : None ,
243327 content : OneOrMany :: one ( AssistantContent :: Text ( Text {
244- text : turn . assistant_response . clone ( ) ,
328+ text : response_text ,
245329 } ) ) ,
246330 } ) ;
247331 }
@@ -299,6 +383,7 @@ mod tests {
299383 tool_name: "analyze" . to_string( ) ,
300384 args_summary: "path: ." . to_string( ) ,
301385 result_summary: "Found rust project" . to_string( ) ,
386+ tool_id: Some ( format!( "tool_{}" , i) ) ,
302387 } ] ,
303388 ) ;
304389 }
0 commit comments