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