ai_agent/services/compact/
prompt.rs1const NO_TOOLS_PREAMBLE: &str = r#"CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.
11
12- Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.
13- You already have all the context you need in the conversation above.
14- Tool calls will be REJECTED and will waste your only turn — you will fail the task.
15- Your entire response must be plain text: an <analysis> block followed by a <summary> block.
16
17"#;
18
19const DETAILED_ANALYSIS_INSTRUCTION_BASE: &str = r#"Before providing your final summary, wrap your analysis in <analysis> tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process:
21
221. Chronologically analyze each message and section of the conversation. For each section thoroughly identify:
23 - The user's explicit requests and intents
24 - Your approach to addressing the user's requests
25 - Key decisions, technical concepts and code patterns
26 - Specific details like:
27 - file names
28 - full code snippets
29 - function signatures
30 - file edits
31 - Errors that you ran into and how you fixed them
32 - Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
332. Double-check for technical accuracy and completeness, addressing each required element thoroughly."#;
34
35const DETAILED_ANALYSIS_INSTRUCTION_PARTIAL: &str = r#"Before providing your final summary, wrap your analysis in <analysis> tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process:
37
381. Analyze the recent messages chronologically. For each section thoroughly identify:
39 - The user's explicit requests and intents
40 - Your approach to addressing the user's requests
41 - Key decisions, technical concepts and code patterns
42 - Specific details like:
43 - file names
44 - full code snippets
45 - function signatures
46 - file edits
47 - Errors that you ran into and how you fixed them
48 - Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
492. Double-check for technical accuracy and completeness, addressing each required element thoroughly."#;
50
51const BASE_COMPACT_PROMPT: &str = r#"Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
53This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context.
54
55DETAILED_ANALYSIS
56
57Your summary should include the following sections:
58
591. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail
602. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed.
613. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Pay special attention to the most recent messages and include full code snippets where applicable and include a summary of why this file read or edit is important.
624. Errors and fixes: List all errors that you ran into, and how you fixed them. Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
635. Problem Solving: Document problems solved and any ongoing troubleshooting efforts.
646. All user messages: List ALL user messages that are not tool results. These are critical for understanding the users' feedback and changing intent.
657. Pending Tasks: Outline any pending tasks that you have explicitly been asked to work on.
668. Current Work: Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant. Include file names and code snippets where applicable.
679. Optional Next Step: List the next step that you will take that is related to the most recent work you were doing. IMPORTANT: ensure that this step is DIRECTLY in line with the user's most recent explicit requests, and the task you were working on immediately before this summary request. If your last task was concluded, then only list next steps if they are explicitly in line with the users request. Do not start on tangential requests or really old requests that were already completed without confirming with the user first.
68 If there is a next step, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no drift in task interpretation.
69
70Here's an example of how your output should be structured:
71
72<example>
73<analysis>
74[Your thought process, ensuring all points are covered thoroughly and accurately]
75</analysis>
76
77<summary>
781. Primary Request and Intent:
79 [Detailed description]
80
812. Key Technical Concepts:
82 - [Concept 1]
83 - [Concept 2]
84 - [...]
85
863. Files and Code Sections:
87 - [File Name 1]
88 - [Summary of why this file is important]
89 - [Summary of the changes made to this file, if any]
90 - [Important Code Snippet]
91 - [File Name 2]
92 - [Important Code Snippet]
93 - [...]
94
954. Errors and fixes:
96 - [Detailed description of error 1]:
97 - [How you fixed the error]
98 - [User feedback on the error if any]
99 - [...]
100
1015. Problem Solving:
102 [Description of solved problems and ongoing troubleshooting]
103
1046. All user messages:
105 - [Detailed non tool use user message]
106 - [...]
107
1087. Pending Tasks:
109 - [Task 1]
110 - [Task 2]
111 - [...]
112
1138. Current Work:
114 [Precise description of current work]
115
1169. Optional Next Step:
117 [Optional Next step to take]
118
119</summary>
120</example>
121
122Please provide your summary based on the conversation so far, following this structure and ensuring precision and thoroughness in your response.
123
124There may be additional summarization instructions provided in the included context. If so, remember to follow those instructions when creating the above summary. Examples of instructions include:
125<example>
126## Compact Instructions
127When summarizing the conversation focus on typescript code changes and also remember the mistakes you made and how you fixed them.
128</example>
129
130<example>
131# Summary instructions
132When you are using compact - please focus on test output and code changes. Include file reads verbatim.
133</example>
134
135REMINDER: Do NOT call any tools. Respond with plain text only — an <analysis> block followed by a <summary> block. Tool calls will be rejected and you will fail the task.
136"#;
137
138const PARTIAL_COMPACT_PROMPT: &str = r#"Your task is to create a detailed summary of the RECENT portion of the conversation — the messages that follow earlier retained context. The earlier messages are being kept intact and do NOT need to be summarized. Focus your summary on what was discussed, learned, and accomplished in the recent messages only.
140
141DETAILED_ANALYSIS
142
143Your summary should include the following sections:
144
1451. Primary Request and Intent: Capture the user's explicit requests and intents from the recent messages
1462. Key Technical Concepts: List important technical concepts, technologies, and frameworks discussed recently.
1473. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Include full code snippets where applicable and include a summary of why this file read or edit is important.
1484. Errors and fixes: List errors encountered and how they were fixed.
1495. Problem Solving: Document problems solved and any ongoing troubleshooting efforts.
1506. All user messages: List ALL user messages from the recent portion that are not tool results.
1517. Pending Tasks: Outline any pending tasks from the recent messages.
1528. Current Work: Describe precisely what was being worked on immediately before this summary request.
1539. Optional Next Step: List the next step related to the most recent work. Include direct quotes from the most recent conversation.
154
155Here's an example of how your output should be structured:
156
157<example>
158<analysis>
159[Your thought process, ensuring all points are covered thoroughly and accurately]
160</analysis>
161
162<summary>
1631. Primary Request and Intent:
164 [Detailed description]
165
1662. Key Technical Concepts:
167 - [Concept 1]
168 - [Concept 2]
169
1703. Files and Code Sections:
171 - [File Name 1]
172 - [Summary of why this file is important]
173 - [Important Code Snippet]
174
1754. Errors and fixes:
176 - [Error description]:
177 - [How you fixed it]
178
1795. Problem Solving:
180 [Description]
181
1826. All user messages:
183 - [Detailed non tool use user message]
184
1857. Pending Tasks:
186 - [Task 1]
187
1888. Current Work:
189 [Precise description of current work]
190
1919. Optional Next Step:
192 [Optional Next step to take]
193
194</summary>
195</example>
196
197Please provide your summary of the recent messages following this structure. There may be additional summarization instructions provided in the included context.
198
199REMINDER: Do NOT call any tools. Respond with plain text only — an <analysis> block followed by a <summary> block."#;
200
201const PARTIAL_COMPACT_UP_TO_PROMPT: &str = r#"Your task is to create a detailed summary of the EARLIER portion of the conversation — the messages that precede the retained later context. The later messages are being kept intact and do NOT need to be summarized. Focus your summary on what was discussed, learned, and accomplished in the earlier messages only.
203
204DETAILED_ANALYSIS
205
206Your summary should include the following sections:
207
2081. Primary Request and Intent: Capture the user's explicit requests and intents from the earlier messages
2092. Key Technical Concepts: List important technical concepts, technologies, and frameworks discussed.
2103. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created.
2114. Errors and fixes: List errors encountered and how they were fixed.
2125. Problem Solving: Document problems solved and ongoing troubleshooting.
2136. All user messages: List ALL user messages from the earlier portion that are not tool results.
2147. Pending Tasks: Outline any pending tasks.
2158. Current Work: Describe what was being worked on.
2169. Context for Continuing Work: Provide key context, decisions, or state needed to continue the work from the earlier portion.
217
218Here's an example of how your output should be structured:
219
220<example>
221<analysis>
222[Your thought process, ensuring all points are covered thoroughly and accurately]
223</analysis>
224
225<summary>
2261. Primary Request and Intent:
227 [Detailed description]
228
2292. Key Technical Concepts:
230 - [Concept 1]
231 - [Concept 2]
232
2333. Files and Code Sections:
234 - [File Name 1]
235 - [Summary of why this file is important]
236 - [Important Code Snippet]
237
2384. Errors and fixes:
239 - [Error description]:
240 - [How you fixed it]
241
2425. Problem Solving:
243 [Description]
244
2456. All user messages:
246 - [Detailed non tool use user message]
247
2487. Pending Tasks:
249 - [Task 1]
250
2518. Current Work:
252 [Precise description of current work]
253
2549. Context for Continuing Work:
255 [Key context needed to continue]
256
257</summary>
258</example>
259
260Please provide your summary of the earlier messages following this structure.
261
262REMINDER: Do NOT call any tools. Respond with plain text only — an <analysis> block followed by a <summary> block."#;
263
264pub fn get_compact_prompt(custom_instructions: Option<&str>) -> String {
266 let prompt = format!(
267 "{}{}{}",
268 NO_TOOLS_PREAMBLE, DETAILED_ANALYSIS_INSTRUCTION_BASE, BASE_COMPACT_PROMPT
269 );
270
271 if let Some(instructions) = custom_instructions {
272 if !instructions.trim().is_empty() {
273 format!("{}\n\n## Custom Instructions\n{}\n", prompt, instructions)
274 } else {
275 prompt
276 }
277 } else {
278 prompt
279 }
280}
281
282pub fn get_partial_compact_prompt(
284 direction: PartialCompactDirection,
285 custom_instructions: Option<&str>,
286) -> String {
287 let analysis_instruction = DETAILED_ANALYSIS_INSTRUCTION_PARTIAL;
288 let base_prompt = match direction {
289 PartialCompactDirection::From => PARTIAL_COMPACT_PROMPT,
290 PartialCompactDirection::UpTo => PARTIAL_COMPACT_UP_TO_PROMPT,
291 };
292
293 let prompt = format!(
294 "{}{}{}",
295 NO_TOOLS_PREAMBLE, analysis_instruction, base_prompt
296 );
297
298 if let Some(instructions) = custom_instructions {
299 if !instructions.trim().is_empty() {
300 format!("{}\n\n## Custom Instructions\n{}\n", prompt, instructions)
301 } else {
302 prompt
303 }
304 } else {
305 prompt
306 }
307}
308
309#[derive(Debug, Clone, Copy, PartialEq)]
311pub enum PartialCompactDirection {
312 From,
314 UpTo,
316}
317
318impl Default for PartialCompactDirection {
319 fn default() -> Self {
320 PartialCompactDirection::From
321 }
322}
323
324pub fn format_compact_summary(summary: &str) -> String {
327 let without_analysis = strip_analysis_tags(summary);
329
330 let with_header = replace_summary_tags(&without_analysis);
332
333 clean_whitespace(&with_header)
335}
336
337fn strip_analysis_tags(text: &str) -> String {
339 let mut result = text.to_string();
341 while let Some(start) = result.find("<analysis>") {
342 if let Some(end) = result[start..].find("</analysis>") {
343 let end_pos = start + end + "</analysis>".len();
344 result.replace_range(start..end_pos, "");
345 } else {
346 break;
347 }
348 }
349 result
350}
351
352fn replace_summary_tags(text: &str) -> String {
354 let mut result = text
355 .replace("<summary>", "Summary:\n")
356 .replace("</summary>", "");
357 result = result.replace("Summary:\n\n", "Summary:\n");
359 result
360}
361
362fn clean_whitespace(text: &str) -> String {
364 let trimmed = text.trim();
365 let mut result = String::with_capacity(trimmed.len());
367 let mut consecutive_newlines = 0;
368 for ch in trimmed.chars() {
369 if ch == '\n' {
370 consecutive_newlines += 1;
371 if consecutive_newlines <= 2 {
372 result.push(ch);
373 }
374 } else {
375 consecutive_newlines = 0;
376 result.push(ch);
377 }
378 }
379 result
380}
381
382pub fn get_compact_user_summary_message(
385 summary: &str,
386 suppress_follow_up_questions: Option<bool>,
387 transcript_path: Option<&str>,
388 recent_preserved: Option<bool>,
389) -> String {
390 let mut message = String::new();
391
392 message.push_str("## Session Continued from Previous Conversation\n\n");
393 message.push_str("The conversation below is a continuation of a previous session. ");
394 message.push_str("Here's a summary of what was discussed before:\n\n");
395 message.push_str(summary);
396
397 if recent_preserved == Some(true) {
398 message.push_str(
399 "\n\nRecent messages are preserved verbatim — they do not need to be re-summarized.",
400 );
401 }
402
403 if let Some(path) = transcript_path {
404 message.push_str(&format!(
405 "\n\nFor the complete conversation transcript, see: {}",
406 path
407 ));
408 }
409
410 if suppress_follow_up_questions == Some(true) {
411 message.push_str(
412 "\n\nPlease continue working on the task without asking follow-up questions. \
413 If you need to take action, do so directly.",
414 );
415 }
416
417 message
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_format_compact_summary_strips_analysis() {
426 let input = r#"<analysis>
427Let me think through this...
428</analysis>
429
430<summary>
4311. Primary Request: Build a tool
432</summary>"#;
433 let result = format_compact_summary(input);
434 assert!(!result.contains("<analysis>"));
435 assert!(!result.contains("</analysis>"));
436 assert!(!result.contains("<summary>"));
437 assert!(!result.contains("</summary>"));
438 assert!(result.contains("Summary:"));
439 assert!(result.contains("1. Primary Request: Build a tool"));
440 }
441
442 #[test]
443 fn test_format_compact_summary_no_analysis() {
444 let input = "<summary>\n1. Request: Test\n</summary>";
445 let result = format_compact_summary(input);
446 assert!(result.contains("Summary:"));
447 assert!(result.contains("1. Request: Test"));
448 }
449
450 #[test]
451 fn test_format_compact_summary_cleans_whitespace() {
452 let input = "<summary>\n\nTest\n\n\n\nMore\n\n\n</summary>";
453 let result = format_compact_summary(input);
454 assert!(!result.contains("\n\n\n"));
456 }
457
458 #[test]
459 fn test_get_compact_prompt_contains_no_tools() {
460 let prompt = get_compact_prompt(None);
461 assert!(prompt.contains("TEXT ONLY"));
462 assert!(prompt.contains("Do NOT call any tools"));
463 }
464
465 #[test]
466 fn test_get_compact_prompt_custom_instructions() {
467 let prompt = get_compact_prompt(Some("Focus on code changes"));
468 assert!(prompt.contains("Focus on code changes"));
469 }
470
471 #[test]
472 fn test_get_compact_prompt_empty_custom_instructions() {
473 let prompt = get_compact_prompt(Some(""));
474 assert!(!prompt.contains("Custom Instructions"));
475 }
476
477 #[test]
478 fn test_get_partial_compact_prompt_from() {
479 let prompt = get_partial_compact_prompt(PartialCompactDirection::From, None);
480 assert!(prompt.contains("RECENT portion"));
481 assert!(prompt.contains("Do NOT call any tools"));
482 }
483
484 #[test]
485 fn test_get_partial_compact_prompt_up_to() {
486 let prompt = get_partial_compact_prompt(PartialCompactDirection::UpTo, None);
487 assert!(prompt.contains("EARLIER portion"));
488 assert!(prompt.contains("Context for Continuing Work"));
489 }
490
491 #[test]
492 fn test_get_compact_user_summary_message_basic() {
493 let msg = get_compact_user_summary_message("Test summary", None, None, None);
494 assert!(msg.contains("Session Continued from Previous Conversation"));
495 assert!(msg.contains("Test summary"));
496 }
497
498 #[test]
499 fn test_get_compact_user_summary_message_suppress() {
500 let msg = get_compact_user_summary_message("Test summary", Some(true), None, None);
501 assert!(msg.contains("without asking follow-up questions"));
502 }
503
504 #[test]
505 fn test_get_compact_user_summary_message_transcript() {
506 let msg = get_compact_user_summary_message(
507 "Test summary",
508 None,
509 Some("/path/to/transcript"),
510 None,
511 );
512 assert!(msg.contains("/path/to/transcript"));
513 }
514
515 #[test]
516 fn test_get_compact_user_summary_message_recent_preserved() {
517 let msg = get_compact_user_summary_message("Test summary", None, None, Some(true));
518 assert!(msg.contains("Recent messages are preserved verbatim"));
519 }
520}