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:
Ryan Ward
2026-05-21 14:30:04 -05:00
co-authored by Claude Opus 4.6
parent 6f54e2cb30
commit f278e53b7e
22 changed files with 914 additions and 113 deletions
+67 -1
View File
@@ -78,7 +78,9 @@ use std::sync::Arc;
use std::time::Duration;
use warp_multi_agent_api::{message, Task, ToolType};
use super::orchestration_events::{OrchestrationEventService, OrchestrationEventServiceEvent};
use super::orchestration_events::{
OrchestrationEventService, OrchestrationEventServiceEvent, PendingEventDetail,
};
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
#[derive(Debug, Clone)]
@@ -1507,6 +1509,9 @@ impl BlocklistAIController {
return;
}
// Handle local subagent events before standard orchestration events.
self.handle_subagent_events(conversation_id, ctx);
if self
.in_flight_response_streams
.has_active_stream_for_conversation(conversation_id, ctx)
@@ -1555,6 +1560,67 @@ impl BlocklistAIController {
}
}
/// Processes local subagent events (questions, answers, summaries) for a conversation.
fn handle_subagent_events(
&mut self,
conversation_id: AIConversationId,
ctx: &mut ModelContext<Self>,
) {
let events = OrchestrationEventService::handle(ctx).update(ctx, |svc, _ctx| {
svc.drain_subagent_events(&conversation_id)
});
for event in events {
match event.detail {
PendingEventDetail::SubagentQuestion {
source_conversation_id,
question_text,
options,
..
} => {
// Auto-answer: pick the first option, or echo the question text
// as a default answer. In a future version, this could invoke the
// parent LLM for a contextual answer.
let answer = options.first().cloned().unwrap_or_else(|| {
format!("Proceed with: {}", question_text)
});
OrchestrationEventService::handle(ctx).update(ctx, |svc, ctx| {
svc.route_answer_to_subagent(source_conversation_id, answer, ctx);
});
}
PendingEventDetail::SubagentAnswer {
answer_text, ..
} => {
// This fires on the child's controller — complete its pending question.
self.complete_ask_user_question_with_answer(answer_text, ctx);
}
PendingEventDetail::SubagentCompletionSummary { .. } => {
// Summary is consumed by the inline panel renderer directly.
// No controller action needed.
}
_ => {}
}
}
}
/// Completes this controller's pending AskUserQuestion with the answer from the parent.
fn complete_ask_user_question_with_answer(
&self,
answer_text: String,
ctx: &mut ModelContext<Self>,
) {
use ai::agent::action_result::AskUserQuestionAnswerItem;
let executor = self.action_model.as_ref(ctx).ask_user_question_executor(ctx);
let answer_item = AskUserQuestionAnswerItem::Answered {
question_id: String::new(),
selected_options: vec![answer_text.clone()],
other_text: answer_text,
};
executor.as_ref(ctx).complete(vec![answer_item]);
}
pub fn resume_conversation(
&mut self,
conversation_id: AIConversationId,