v1.5.0: Inline subagent panels, Bedrock compaction fixes, context window debug view
New features: - Inline subagent panels with expand/collapse and click-to-toggle - /context slash command to inspect bedrock_message_history - Child-to-parent question routing with auto-answer for subagents - Subagent token usage and cost merging into parent conversation - Randomized session-colored user avatar silhouettes Bug fixes: - Bedrock: remove orphaned tool_results after compaction - Bedrock: self-healing exchange lookup for out-of-order streaming - Bedrock: append continuation prompt when conversation ends with assistant - Duration sanity check rejects epoch-time artifacts from session restore - Cache hit rate calculation uses actual total_input_tokens - Hide "Time to first token" when value is zero Improvements: - Demote verbose bedrock-debug logs to debug/trace levels - Bedrock tool usage counting falls back to action counting - Remove logout menu item from workspace menu 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
6f54e2cb30
commit
f278e53b7e
@@ -698,6 +698,20 @@ impl Input {
|
||||
ctx.dispatch_typed_action(&TerminalAction::ToggleUsageFooter);
|
||||
}
|
||||
}
|
||||
_context if command.name == commands::CONTEXT.name => {
|
||||
let history = BlocklistAIHistoryModel::handle(ctx);
|
||||
let conversation = history
|
||||
.as_ref(ctx)
|
||||
.active_conversation(self.terminal_view_id);
|
||||
if conversation.is_none() {
|
||||
show_error_toast(
|
||||
"Cannot show context: no active conversation".to_owned(),
|
||||
ctx,
|
||||
);
|
||||
} else {
|
||||
ctx.dispatch_typed_action(&TerminalAction::ToggleContextView);
|
||||
}
|
||||
}
|
||||
_fork if command.name == commands::FORK.name => {
|
||||
let Some(conversation_id) = self
|
||||
.ai_context_model
|
||||
|
||||
@@ -2586,6 +2586,9 @@ pub struct TerminalView {
|
||||
/// Cached view ids for usage footers keyed by the AI block view id that owns them.
|
||||
usage_footer_view_ids: HashMap<EntityId, EntityId>,
|
||||
|
||||
/// View ID of the context window debug view, if visible.
|
||||
context_view_id: Option<EntityId>,
|
||||
|
||||
// Whether the block onboarding view is active or not.
|
||||
block_onboarding_active: bool,
|
||||
|
||||
@@ -4089,6 +4092,7 @@ impl TerminalView {
|
||||
active_filter_editor_block_index: None,
|
||||
rich_content_views: Vec::new(),
|
||||
usage_footer_view_ids: Default::default(),
|
||||
context_view_id: None,
|
||||
block_onboarding_active: false,
|
||||
onboarding_agentic_suggestions_block: None,
|
||||
onboarding_prompt_block: None,
|
||||
@@ -5621,6 +5625,18 @@ impl TerminalView {
|
||||
};
|
||||
|
||||
let tool_usage = conversation.tool_usage_metadata();
|
||||
// For Bedrock conversations, the server doesn't send tool_usage_metadata.
|
||||
// Fall back to counting actions from the conversation exchanges.
|
||||
let tool_call_count = if tool_usage.total_tool_calls() > 0 {
|
||||
tool_usage.total_tool_calls()
|
||||
} else {
|
||||
conversation.count_all_actions() as i32
|
||||
};
|
||||
let commands_executed = if tool_usage.run_command_stats.commands_executed > 0 {
|
||||
tool_usage.run_command_stats.commands_executed
|
||||
} else {
|
||||
conversation.count_command_actions() as i32
|
||||
};
|
||||
let time_to_first_token_ms = conversation.time_to_first_token_for_last_user_query_ms();
|
||||
let total_agent_response_time_ms =
|
||||
conversation.total_agent_response_time_since_last_user_query_ms();
|
||||
@@ -5631,20 +5647,22 @@ impl TerminalView {
|
||||
let total_cache_read_tokens: u32 = token_usage_list.iter().map(|u| u.input_cache_read).sum();
|
||||
let total_cache_write_tokens: u32 =
|
||||
token_usage_list.iter().map(|u| u.input_cache_write).sum();
|
||||
let total_input_tokens: u32 = token_usage_list.iter().map(|u| u.total_input).sum();
|
||||
let estimated_cost_cents: f32 = token_usage_list.iter().map(|u| u.cost_in_cents).sum();
|
||||
|
||||
let conversation_usage_info = ConversationUsageInfo {
|
||||
tool_calls: tool_usage.total_tool_calls(),
|
||||
tool_calls: tool_call_count,
|
||||
models: conversation.token_usage().to_vec(),
|
||||
context_window_usage: conversation.context_window_usage(),
|
||||
files_changed: tool_usage.apply_file_diff_stats.files_changed,
|
||||
lines_added: tool_usage.apply_file_diff_stats.lines_added,
|
||||
lines_removed: tool_usage.apply_file_diff_stats.lines_removed,
|
||||
commands_executed: tool_usage.run_command_stats.commands_executed,
|
||||
commands_executed,
|
||||
current_context_tokens: conversation.current_context_tokens(),
|
||||
estimated_cost_cents,
|
||||
total_cache_read_tokens,
|
||||
total_cache_write_tokens,
|
||||
total_input_tokens,
|
||||
};
|
||||
|
||||
let timing_info = TimingInfo {
|
||||
@@ -5729,6 +5747,51 @@ impl TerminalView {
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_context_view(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
use crate::ai::blocklist::usage::context_window_view::ContextWindowView;
|
||||
|
||||
// If already showing, remove it
|
||||
if let Some(view_id) = self.context_view_id.take() {
|
||||
let mut model = self.model.lock();
|
||||
model.block_list_mut().remove_rich_content(view_id);
|
||||
drop(model);
|
||||
self.rich_content_views.retain(|rc| rc.view_id() != view_id);
|
||||
ctx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the active conversation's bedrock_message_history
|
||||
let conversation_id = self
|
||||
.agent_view_controller
|
||||
.as_ref(ctx)
|
||||
.agent_view_state()
|
||||
.active_conversation_id();
|
||||
|
||||
let Some(conversation_id) = conversation_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
let messages = BlocklistAIHistoryModel::as_ref(ctx)
|
||||
.conversation(&conversation_id)
|
||||
.map(|conv| conv.bedrock_message_history().to_vec())
|
||||
.unwrap_or_default();
|
||||
|
||||
let context_view = ctx.add_view(|_| ContextWindowView::new(messages));
|
||||
let view_id = context_view.id();
|
||||
self.context_view_id = Some(view_id);
|
||||
|
||||
self.insert_rich_content(
|
||||
None,
|
||||
context_view,
|
||||
None,
|
||||
RichContentInsertionPosition::Append {
|
||||
insert_below_long_running_block: true,
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
/// Returns true if the window is wide enough to auto-open side panels.
|
||||
pub fn can_auto_open_panel(&self) -> bool {
|
||||
self.size_info.pane_width_px().as_f32() > MINIMUM_WIDTH_TO_AUTO_OPEN_PANE
|
||||
@@ -24407,6 +24470,7 @@ impl TypedActionView for TerminalView {
|
||||
| AwsCliNotInstalledBanner(_)
|
||||
| ExecuteRewindFromInlineMenu { .. }
|
||||
| ToggleUsageFooter
|
||||
| ToggleContextView
|
||||
| RevealChildAgent { .. }
|
||||
| OpenCLIAgentRichInput
|
||||
| ToggleSessionRecording => Empty,
|
||||
@@ -25428,6 +25492,9 @@ impl TypedActionView for TerminalView {
|
||||
ToggleUsageFooter => {
|
||||
self.toggle_usage_footer(ctx);
|
||||
}
|
||||
ToggleContextView => {
|
||||
self.toggle_context_view(ctx);
|
||||
}
|
||||
RevealChildAgent { conversation_id } => {
|
||||
ctx.emit(Event::RevealChildAgent {
|
||||
conversation_id: *conversation_id,
|
||||
|
||||
@@ -422,6 +422,8 @@ pub enum TerminalAction {
|
||||
AwsCliNotInstalledBanner(AwsCliNotInstalledBannerAction),
|
||||
/// Toggle the usage footer on the last AI block in the active conversation.
|
||||
ToggleUsageFooter,
|
||||
/// Toggle the context window debug view showing bedrock_message_history.
|
||||
ToggleContextView,
|
||||
/// Reveal a hidden child agent pane from the orchestrator status card.
|
||||
RevealChildAgent {
|
||||
conversation_id: AIConversationId,
|
||||
@@ -702,6 +704,7 @@ impl fmt::Debug for TerminalAction {
|
||||
AwsBedrockLoginBanner(action) => write!(f, "AwsBedrockLoginBanner({action:?})"),
|
||||
AwsCliNotInstalledBanner(action) => write!(f, "AwsCliNotInstalledBanner({action:?})"),
|
||||
ToggleUsageFooter => write!(f, "ToggleUsageFooter"),
|
||||
ToggleContextView => write!(f, "ToggleContextView"),
|
||||
RevealChildAgent { .. } => write!(f, "RevealChildAgent"),
|
||||
ToggleSessionRecording => write!(f, "ToggleSessionRecording"),
|
||||
OpenCLIAgentRichInput => write!(f, "OpenCLIAgentRichInput"),
|
||||
|
||||
Reference in New Issue
Block a user