1pub use super::session_store::*;
7
8use super::{fence_external_system_messages_for_resume, Message, PawanAgent, Role};
9use crate::config::PawanConfig;
10use crate::tools::ToolDefinition;
11use crate::Result;
12
13impl PawanAgent {
14 pub fn history(&self) -> &[Message] {
15 &self.history
16 }
17
18 pub fn save_session(&self) -> Result<String> {
20 let mut session = Session::new(&self.config.model);
21 session.messages = self.history.clone();
22 session.total_tokens = self.context_tokens_estimate as u64;
23 session.save()?;
24 Ok(session.id)
25 }
26
27 pub fn resume_session(&mut self, session_id: &str) -> Result<()> {
29 let session = Session::load(session_id)?;
30 self.history = session.messages;
31 self.context_tokens_estimate = session.total_tokens as usize;
32 self.session_id = session_id.to_string();
35 fence_external_system_messages_for_resume(&mut self.history);
36 Ok(())
37 }
38
39 pub async fn archive_to_eruka(&self) -> Result<()> {
43 let Some(eruka) = &self.eruka else {
44 return Ok(());
45 };
46 let mut session = Session::new(&self.config.model);
47 session.id = self.session_id.clone();
48 session.messages = self.history.clone();
49 session.total_tokens = self.context_tokens_estimate as u64;
50 eruka.archive_session(&session).await
51 }
52
53 pub(crate) fn history_snapshot_for_eruka(history: &[Message]) -> String {
57 let mut out = String::with_capacity(2048);
58 for msg in history {
59 let prefix = match msg.role {
60 Role::User => "U: ",
61 Role::Assistant => "A: ",
62 Role::Tool => "T: ",
63 Role::System => "S: ",
64 };
65 let body: String = msg.content.chars().take(200).collect();
66 out.push_str(prefix);
67 out.push_str(&body);
68 out.push('\n');
69 if out.len() > 4000 {
70 break;
71 }
72 }
73 out
74 }
75
76 pub fn config(&self) -> &PawanConfig {
78 &self.config
79 }
80
81 pub fn clear_history(&mut self) {
83 self.history.clear();
84 }
85
86 pub(crate) fn prune_history(&mut self) {
94 let len = self.history.len();
95 if len <= 5 {
96 return; }
98
99 let keep_end = 4;
100 let start = 1; let end = len - keep_end;
102 let pruned_count = end - start;
103
104 let mut scored: Vec<(f32, &Message)> = self.history[start..end]
106 .iter()
107 .map(|msg| {
108 let score = Self::message_importance(msg);
109 (score, msg)
110 })
111 .collect();
112 scored.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
113
114 let mut summary = String::with_capacity(2048);
116 for (score, msg) in &scored {
117 let prefix = match msg.role {
118 Role::User => "User: ",
119 Role::Assistant => "Assistant: ",
120 Role::Tool => {
121 if *score > 0.7 {
122 "Tool error: "
123 } else {
124 "Tool: "
125 }
126 }
127 Role::System => "System: ",
128 };
129 let chunk: String = msg.content.chars().take(200).collect();
130 summary.push_str(prefix);
131 summary.push_str(&chunk);
132 summary.push('\n');
133 if summary.len() > 2000 {
134 let safe_end = summary
135 .char_indices()
136 .take_while(|(i, _)| *i <= 2000)
137 .last()
138 .map(|(i, c)| i + c.len_utf8())
139 .unwrap_or(0);
140 summary.truncate(safe_end);
141 break;
142 }
143 }
144
145 let summary_msg = Message {
146 role: Role::System,
147 content: format!(
148 "Previous conversation summary (pruned {} messages, importance-ranked): {}",
149 pruned_count, summary
150 ),
151 tool_calls: vec![],
152 tool_result: None,
153 };
154
155 self.history.drain(start..end);
156 self.history.insert(start, summary_msg);
157
158 tracing::info!(
159 pruned = pruned_count,
160 context_estimate = self.context_tokens_estimate,
161 "Pruned messages from history (importance-ranked)"
162 );
163 }
164
165 pub(crate) fn message_importance(msg: &Message) -> f32 {
168 match msg.role {
169 Role::User => 0.6, Role::System => 0.3, Role::Assistant => {
172 if msg.content.contains("error") || msg.content.contains("Error") {
173 0.8
174 } else {
175 0.4
176 }
177 }
178 Role::Tool => {
179 if let Some(ref result) = msg.tool_result {
180 if !result.success {
181 0.9
182 }
183 else {
185 0.2
186 } } else {
188 0.3
189 }
190 }
191 }
192 }
193
194 pub fn add_message(&mut self, message: Message) {
196 self.history.push(message);
197 }
198
199 pub fn switch_model(&mut self, model: &str) -> Result<()> {
201 self.config.model = model.to_string();
202 let system_prompt = self.config.get_system_prompt_checked()?;
203 self.backend = Self::create_backend(&self.config, &system_prompt);
204 tracing::info!(model = model, "Model switched at runtime");
205 Ok(())
206 }
207
208 pub fn model_name(&self) -> &str {
210 &self.config.model
211 }
212
213 pub fn session_id(&self) -> &str {
215 &self.session_id
216 }
217
218 pub fn get_tool_definitions(&self) -> Vec<ToolDefinition> {
220 self.tools.get_definitions()
221 }
222}