Skip to main content

ai_agent/services/compact/
prompt.rs

1// Source: ~/claudecode/openclaudecode/src/services/compact/prompt.ts
2//! Compact prompt templates for conversation summarization.
3//!
4//! Three prompt variants:
5//! - BASE_COMPACT_PROMPT: Full conversation summary
6//! - PARTIAL_COMPACT_PROMPT: Recent messages only (after retained context)
7//! - PARTIAL_COMPACT_UP_TO_PROMPT: Earlier messages (before retained suffix)
8
9/// Aggressive no-tools preamble
10const 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
19/// Analysis instruction for full compaction
20const 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
35/// Analysis instruction for partial compaction
36const 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
51/// Base compact prompt - full conversation summary
52const 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
138/// Partial compact prompt - recent messages only
139const 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
201/// Partial compact up-to prompt - earlier messages (before retained suffix)
202const 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
264/// Get the full compact prompt with optional custom instructions
265pub 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
282/// Get the partial compact prompt
283pub 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/// Direction for partial compaction
310#[derive(Debug, Clone, Copy, PartialEq)]
311pub enum PartialCompactDirection {
312    /// Summarize messages AFTER pivot index (keeps earlier context)
313    From,
314    /// Summarize messages BEFORE pivot index (keeps later context)
315    UpTo,
316}
317
318impl Default for PartialCompactDirection {
319    fn default() -> Self {
320        PartialCompactDirection::From
321    }
322}
323
324/// Format the compact summary by stripping analysis tags and cleaning whitespace.
325/// Matches TypeScript's formatCompactSummary()
326pub fn format_compact_summary(summary: &str) -> String {
327    // Strip <analysis>...</analysis> section (drafting scratchpad)
328    let without_analysis = strip_analysis_tags(summary);
329
330    // Replace <summary>...</summary> with "Summary:" header
331    let with_header = replace_summary_tags(&without_analysis);
332
333    // Clean extra whitespace
334    clean_whitespace(&with_header)
335}
336
337/// Strip <analysis> tags and their content from the summary
338fn strip_analysis_tags(text: &str) -> String {
339    // Remove everything between <analysis> and </analysis> (inclusive)
340    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
352/// Replace <summary> tags with "Summary:" header
353fn replace_summary_tags(text: &str) -> String {
354    let mut result = text
355        .replace("<summary>", "Summary:\n")
356        .replace("</summary>", "");
357    // Clean up the Summary: header formatting
358    result = result.replace("Summary:\n\n", "Summary:\n");
359    result
360}
361
362/// Clean extra whitespace: trim, collapse multiple blank lines to one
363fn clean_whitespace(text: &str) -> String {
364    let trimmed = text.trim();
365    // Collapse 3+ consecutive newlines to 2 (one blank line)
366    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
382/// Get the user-facing compact summary message.
383/// Matches TypeScript's getCompactUserSummaryMessage()
384pub 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        // Should not have 3+ consecutive newlines
455        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}