Fix ACD turn summary loss and add /dump command

ACD (Aggressive Context Dehydration) fixes:
- Fixed dehydrate_context() to extract turn summary from context window
  instead of using the passed-in final_response (which contained only
  the timing footer, not the actual LLM response)
- Removed final_response parameter from dehydrate_context() since it
  now self-extracts the last assistant message as the summary
- This ensures the actual turn summary is preserved after dehydration,
  not just the timing footer

New /dump command:
- Added /dump command to dump entire context window to tmp/ for debugging
- Shows message index, role, kind, content length, and full content
- Available in both console and machine modes

UTF-8 safety:
- Fixed truncate_to_word_boundary() to use character indices instead of
  byte indices, preventing panics on multi-byte UTF-8 characters
- Added UTF-8 string slicing guidance to AGENTS.md

Agent: g3
This commit is contained in:
Dhanji R. Prasanna
2026-01-12 05:13:02 +05:30
parent ac17b95b24
commit f415dbb84b
14 changed files with 1771 additions and 27 deletions

View File

@@ -94,6 +94,8 @@ pub struct Message {
pub images: Vec<ImageContent>,
#[serde(skip)]
pub id: String,
#[serde(skip)]
pub kind: MessageKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_control: Option<CacheControl>,
}
@@ -106,6 +108,20 @@ pub enum MessageRole {
Assistant,
}
/// Special message kinds for context management (ACD)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum MessageKind {
/// Regular conversation message
#[default]
Regular,
/// Dehydrated context stub (contains fragment reference)
DehydratedStub,
/// Summary of dehydrated context (the response that followed dehydration)
Summary,
/// Rehydrated content (restored from a fragment)
Rehydrated,
}
/// Image content for multimodal messages
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageContent {
@@ -242,6 +258,7 @@ impl Message {
content,
images: Vec::new(),
id: Self::generate_id(),
kind: MessageKind::Regular,
cache_control: None,
}
}
@@ -257,10 +274,33 @@ impl Message {
content,
images: Vec::new(),
id: Self::generate_id(),
kind: MessageKind::Regular,
cache_control: Some(cache_control),
}
}
/// Create a new message with a specific kind (for ACD)
pub fn with_kind(role: MessageRole, content: String, kind: MessageKind) -> Self {
Self {
role,
content,
images: Vec::new(),
id: Self::generate_id(),
kind,
cache_control: None,
}
}
/// Check if this message is a dehydrated stub
pub fn is_dehydrated_stub(&self) -> bool {
self.kind == MessageKind::DehydratedStub
}
/// Check if this message is a summary
pub fn is_summary(&self) -> bool {
self.kind == MessageKind::Summary
}
/// Create a message with cache control, with provider validation
pub fn with_cache_control_validated(
role: MessageRole,