per-message context headers, memory notes, conversation continuity

conversations API path now injects per-message context headers with
live timestamps, room name, and memory notes. this replaces the
template variables in agent instructions which were frozen at
creation time.

memory notes (topical + recent backfill) loaded before each response
in the conversations path — was previously only in the legacy path.

context hint seeds new conversations with recent room history after
resets, so sol doesn't lose conversational continuity on sneeze.

tool call results now logged with preview + length for debugging.
reset_all() clears both in-memory and sqlite conversation state.
This commit is contained in:
2026-03-22 15:00:43 +00:00
parent 904ffa2d4d
commit 7bf9e25361
4 changed files with 170 additions and 29 deletions

View File

@@ -69,12 +69,15 @@ impl ConversationRegistry {
/// Get or create a conversation for a room. Returns the conversation ID.
/// If a conversation doesn't exist yet, creates one with the first message.
/// `context_hint` is prepended to the first message on new conversations,
/// giving the agent recent conversation history for continuity after resets.
pub async fn send_message(
&self,
room_id: &str,
message: ConversationInput,
is_dm: bool,
mistral: &MistralClient,
context_hint: Option<&str>,
) -> Result<ConversationResponse, String> {
let mut mapping = self.mapping.lock().await;
@@ -107,11 +110,25 @@ impl ConversationRegistry {
Ok(response)
} else {
// New conversation — create
// New conversation — create (with optional context hint for continuity)
let agent_id = self.agent_id.lock().await.clone();
let inputs = if let Some(hint) = context_hint {
// Prepend recent conversation history to the first message
match message {
ConversationInput::Text(text) => {
ConversationInput::Text(format!(
"[recent conversation for context]\n{hint}\n\n[current message]\n{text}"
))
}
other => other,
}
} else {
message
};
let req = CreateConversationRequest {
inputs: message,
inputs,
model: if agent_id.is_none() {
Some(self.model.clone())
} else {
@@ -194,6 +211,16 @@ impl ConversationRegistry {
}
}
/// Reset ALL conversations (e.g., after agent recreation).
/// Clears both in-memory mappings and SQLite.
pub async fn reset_all(&self) {
let mut mapping = self.mapping.lock().await;
let count = mapping.len();
mapping.clear();
self.store.delete_all_conversations();
info!(count, "Reset all conversations");
}
/// Reset a room's conversation (e.g., after compaction).
/// Removes the mapping so the next message creates a fresh conversation.
pub async fn reset(&self, room_id: &str) {