v1.4.0: Auto-compact streaming, Bedrock summarization support, subagent orchestration, and Galaxy rebrand continuation
Major features: - Auto-compact: triggers conversation summarization when context window >= 85%, compacts Bedrock message history to a summary pair, and tracks live context tokens - Bedrock summarization: plumbs `is_summarization` flag through translator/client/response pipeline, handles SummarizeConversation input type, and marks `summarized` in metadata - Session restore: rebuilds bedrock_message_history from persisted task messages via newly-public `convert_proto_message`, preventing empty history on reconnect - Subagent orchestration: adds SubagentQuestion/Answer/CompletionSummary event types, parent-child question routing with depth limits, retry counting, and drain methods - Summarization UI: inline SummarizationView in AI blocks with progress/finished states Refactors: - Rename WarpTheme → GalaxyTheme across ~100 files (rebrand continuation) - Rename warp_home_config_dir → galaxy_home_config_dir and related path functions - Predefined rules: replace "System Defined Rule #N" with descriptive names (e.g. "Correctness Over Speed", "Never Guess") and add lookup helpers - Usage view: replace cumulative input/output token display with live context tokens, cache hit rate calculation, and separate cache read/write stats - Telemetry: remove verbose doc comments, simplify trait definitions - Facts view: simplify delete permission check (always allow local deletion) - Remove warp_managed_paths_watcher.rs (dead code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
eaa2ddc75e
commit
6f54e2cb30
@@ -1995,6 +1995,9 @@ impl BlocklistAIController {
|
||||
request_params.parent_agent_id = parent_agent_id;
|
||||
request_params.agent_name = agent_name;
|
||||
request_params.bedrock_message_history = bedrock_history;
|
||||
request_params.is_summarization = request_input
|
||||
.all_inputs()
|
||||
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
|
||||
|
||||
let server_conversation_token_for_identifiers =
|
||||
conversation_data.server_conversation_token.clone();
|
||||
@@ -2020,9 +2023,13 @@ impl BlocklistAIController {
|
||||
let input_contains_user_query = request_input
|
||||
.all_inputs()
|
||||
.any(|input| input.is_user_query());
|
||||
let input_is_summarization = request_input
|
||||
.all_inputs()
|
||||
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
|
||||
ctx.subscribe_to_model(&response_stream, move |me, event, ctx| {
|
||||
me.handle_response_stream_event(
|
||||
input_contains_user_query,
|
||||
input_is_summarization,
|
||||
event,
|
||||
&response_stream_clone,
|
||||
ctx,
|
||||
@@ -2212,6 +2219,7 @@ impl BlocklistAIController {
|
||||
fn handle_response_stream_event(
|
||||
&mut self,
|
||||
did_input_contain_user_query: bool,
|
||||
is_summarization_request: bool,
|
||||
event: &ResponseStreamEvent,
|
||||
response_stream: &ModelHandle<ResponseStream>,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
@@ -2319,11 +2327,88 @@ impl BlocklistAIController {
|
||||
let history_model = BlocklistAIHistoryModel::handle(ctx);
|
||||
history_model.update(ctx, |history_model, _| {
|
||||
if let Some(conversation) = history_model.conversation_mut(&conversation_id) {
|
||||
*conversation.bedrock_message_history_mut() = new_history;
|
||||
log::info!(
|
||||
"[bedrock] Updated conversation bedrock history: {} messages",
|
||||
conversation.bedrock_message_history().len()
|
||||
);
|
||||
// If this was a summarization request, compact the
|
||||
// history to just the summary instead of keeping
|
||||
// the full message list. This is what actually
|
||||
// frees up context window space.
|
||||
let is_summarization = is_summarization_request;
|
||||
|
||||
if is_summarization {
|
||||
// Extract the assistant's summary from the last
|
||||
// message in the history (the response).
|
||||
let summary_text = new_history
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|msg| {
|
||||
use crate::ai::bedrock::convert::{
|
||||
MessageContent, MessageRole,
|
||||
};
|
||||
if msg.role == MessageRole::Assistant {
|
||||
if let MessageContent::Text(text) =
|
||||
&msg.content
|
||||
{
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(summary) = summary_text {
|
||||
use crate::ai::bedrock::convert::{
|
||||
ConversationMessage, MessageContent,
|
||||
MessageRole,
|
||||
};
|
||||
let assistant_reply = "Understood. I have the context from our previous conversation. How can I help you next?";
|
||||
let user_msg = format!(
|
||||
"Here is a summary of our conversation so far:\n\n{summary}"
|
||||
);
|
||||
let compacted = vec![
|
||||
ConversationMessage {
|
||||
role: MessageRole::User,
|
||||
content: MessageContent::Text(user_msg.clone()),
|
||||
},
|
||||
ConversationMessage {
|
||||
role: MessageRole::Assistant,
|
||||
content: MessageContent::Text(
|
||||
assistant_reply.to_string()
|
||||
),
|
||||
},
|
||||
];
|
||||
log::info!(
|
||||
"[bedrock] Compacted conversation history from {} messages to {} (summary)",
|
||||
new_history.len(),
|
||||
compacted.len()
|
||||
);
|
||||
*conversation.bedrock_message_history_mut() =
|
||||
compacted;
|
||||
|
||||
// Estimate new context size from the compacted content.
|
||||
// ~4 chars per token is a reasonable approximation.
|
||||
let estimated_tokens = ((user_msg.len() + assistant_reply.len()) / 4) as u32;
|
||||
let max_context = crate::ai::bedrock::response_translator::context_window_for_model("claude-opus-4-6-20250514[1m]");
|
||||
let new_usage = estimated_tokens as f32 / max_context as f32;
|
||||
conversation.set_context_window_usage(new_usage);
|
||||
conversation.set_current_context_tokens(estimated_tokens);
|
||||
log::info!(
|
||||
"[bedrock] Post-compact context estimate: ~{} tokens ({:.1}% of context window)",
|
||||
estimated_tokens,
|
||||
new_usage * 100.0
|
||||
);
|
||||
} else {
|
||||
*conversation.bedrock_message_history_mut() =
|
||||
new_history;
|
||||
}
|
||||
} else {
|
||||
*conversation.bedrock_message_history_mut() =
|
||||
new_history;
|
||||
log::info!(
|
||||
"[bedrock] Updated conversation bedrock history: {} messages",
|
||||
conversation.bedrock_message_history().len()
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2800,6 +2885,42 @@ impl BlocklistAIController {
|
||||
});
|
||||
ctx.emit(BlocklistAIControllerEvent::FreeTierLimitCheckTriggered);
|
||||
}
|
||||
|
||||
// Auto-compact: trigger summarization when context window usage >= 85%.
|
||||
let should_auto_compact = {
|
||||
let history_model = BlocklistAIHistoryModel::as_ref(ctx);
|
||||
history_model
|
||||
.conversation(&conversation_id)
|
||||
.is_some_and(|conversation| {
|
||||
let is_summarization_request = conversation
|
||||
.latest_exchange()
|
||||
.is_some_and(|exchange| {
|
||||
exchange
|
||||
.input
|
||||
.iter()
|
||||
.any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. }))
|
||||
});
|
||||
conversation.context_window_usage() >= 0.85
|
||||
&& !conversation.has_pending_auto_compact()
|
||||
&& !is_summarization_request
|
||||
})
|
||||
};
|
||||
|
||||
if should_auto_compact {
|
||||
log::info!(
|
||||
"[auto-compact] Context window usage >= 85% for conversation {:?}, triggering summarization",
|
||||
conversation_id
|
||||
);
|
||||
BlocklistAIHistoryModel::handle(ctx).update(ctx, |history_model, _| {
|
||||
if let Some(conversation) = history_model.conversation_mut(&conversation_id) {
|
||||
conversation.set_has_pending_auto_compact(true);
|
||||
}
|
||||
});
|
||||
self.send_slash_command_request(
|
||||
SlashCommandRequest::Summarize { prompt: None },
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user