diff --git a/app/src/ai/bedrock/request_translator.rs b/app/src/ai/bedrock/request_translator.rs index 956ca32f..e377cd27 100644 --- a/app/src/ai/bedrock/request_translator.rs +++ b/app/src/ai/bedrock/request_translator.rs @@ -1953,7 +1953,7 @@ pub fn default_tool_definitions() -> Vec { }, ToolDefinition { name: "run_agents".to_string(), - description: "Start one or more child agents that share run-wide configuration. Use independent, uniquely named tasks and do not launch duplicate agents for follow-up work. Omitted run-wide fields use the embedded local child runtime and inherit the parent model. After launch, call wait_for_events when you need child-agent results instead of repeating their work yourself.".to_string(), + description: "Start one or more child agents that share run-wide configuration. Use independent, uniquely named tasks and do not launch duplicate agents for follow-up work. Omitted run-wide fields use the embedded local child runtime and the configured terminal model. After launch, call wait_for_events when you need child-agent results instead of repeating their work yourself.".to_string(), input_schema: serde_json::json!({ "type": "object", "properties": { @@ -1970,7 +1970,7 @@ pub fn default_tool_definitions() -> Vec { "required": ["skill", "reference_type"] } }, - "model_id": { "type": "string", "default": "", "description": "Optional child model override; empty inherits the parent model" }, + "model_id": { "type": "string", "default": "", "description": "Optional child model override; empty uses the configured terminal model" }, "harness_type": { "type": "string", "default": "", "description": "Optional harness identifier; empty selects the embedded local child runtime" }, "execution_mode": { "type": "object", diff --git a/app/src/ai/blocklist/controller.rs b/app/src/ai/blocklist/controller.rs index e247bacf..95b02785 100644 --- a/app/src/ai/blocklist/controller.rs +++ b/app/src/ai/blocklist/controller.rs @@ -126,7 +126,8 @@ const PROGRESSIVE_SUMMARY_PROMPT: &str = "Summarize the following conversation h #[derive(Clone)] struct ProgressiveSummaryCandidate { - model_id: String, + selection_model_id: String, + request_model_id: String, context_limit: u32, provider_config: ProviderConfig, } @@ -134,18 +135,32 @@ struct ProgressiveSummaryCandidate { struct ActiveProgressiveSummaryPlan { split_point: usize, retained_messages_count: usize, + trigger_context_tokens: u32, active_context_limit: u32, persistent_context_tokens: u32, candidates: Vec, + summarized_prefix: Vec, summarize_messages: Vec, } +#[derive(Clone, PartialEq, Eq)] struct ActiveProviderRunIdentity { conversation_id: AIConversationId, stream_id: ResponseStreamId, run_id: ProviderRunId, } +enum ActiveProviderProgressiveSummaryState { + InFlight { + identity: ActiveProviderRunIdentity, + }, + Ready { + identity: ActiveProviderRunIdentity, + plan: ActiveProgressiveSummaryPlan, + result: anyhow::Result<(String, Usage, String)>, + }, +} + fn configured_llm_context_limit(info: &LLMInfo) -> u32 { [ info.context_window.default_max, @@ -157,6 +172,31 @@ fn configured_llm_context_limit(info: &LLMInfo) -> u32 { .unwrap_or(128_000) } +fn progressive_summary_candidate( + selection_model_id: String, + context_limit: u32, + provider_config: ProviderConfig, +) -> ProgressiveSummaryCandidate { + // ChatGPT reasoning variants use a synthetic picker ID such as + // `gpt-5.6-luna::reasoning::low`. The resolved provider config retains the actual wire model + // and carries reasoning effort separately, so never send the picker ID to the provider. + let request_model_id = match &provider_config { + ProviderConfig::OpenAI(config) => config + .model + .as_deref() + .filter(|model| !model.trim().is_empty()) + .unwrap_or(&selection_model_id) + .to_owned(), + ProviderConfig::Bedrock(_) | ProviderConfig::None => selection_model_id.clone(), + }; + ProgressiveSummaryCandidate { + selection_model_id, + request_model_id, + context_limit, + provider_config, + } +} + fn estimated_text_tokens(text: &str) -> u32 { u32::try_from(text.chars().count().div_ceil(4)).unwrap_or(u32::MAX) } @@ -806,6 +846,7 @@ struct ActiveProviderRun { action_context: ProviderActionContext, messages_sent: Arc>>, persistence_offset: usize, + last_progressive_summary_failure_context_tokens: Option, } impl ActiveProviderRun { @@ -1102,6 +1143,7 @@ struct ActiveProviderRunCheckpoint { response_config: RuntimeResponseConfig, action_context: ProviderActionContext, persistence_offset: usize, + last_progressive_summary_failure_context_tokens: Option, } struct PreparedRestoredProviderRun { @@ -1128,6 +1170,8 @@ impl ActiveProviderRunCheckpoint { response_config: run.response_config.clone(), action_context: run.action_context.clone(), persistence_offset: run.persistence_offset, + last_progressive_summary_failure_context_tokens: run + .last_progressive_summary_failure_context_tokens, }) } @@ -1152,6 +1196,8 @@ struct ActiveProviderRunSnapshot { did_input_contain_user_query: bool, persistence_offset: usize, #[serde(default)] + last_progressive_summary_failure_context_tokens: Option, + #[serde(default)] cancellation_reason: Option, committed_provider_batch: Option, #[serde(default)] @@ -1195,6 +1241,8 @@ impl ActiveProviderRunSnapshot { root_task_id: slot.root_task_id.clone(), did_input_contain_user_query: slot.did_input_contain_user_query, persistence_offset: checkpoint.persistence_offset, + last_progressive_summary_failure_context_tokens: checkpoint + .last_progressive_summary_failure_context_tokens, cancellation_reason: slot.cancellation_reason, committed_provider_batch: slot.committed_provider_batch.clone(), finished_provider_batch: slot.finished_provider_batch.clone(), @@ -2093,6 +2141,8 @@ pub struct BlocklistAIController { in_flight_response_streams: PendingResponseStreams, active_provider_runs: HashMap, + active_provider_progressive_summaries: + HashMap, queued_provider_runs: HashMap>, restoring_provider_runs: HashSet, restoring_provider_command_completions: @@ -2604,6 +2654,7 @@ impl BlocklistAIController { terminal_model, in_flight_response_streams: PendingResponseStreams::new(), active_provider_runs: HashMap::new(), + active_provider_progressive_summaries: HashMap::new(), queued_provider_runs: HashMap::new(), restoring_provider_runs: HashSet::new(), restoring_provider_command_completions: HashMap::new(), @@ -4341,6 +4392,18 @@ impl BlocklistAIController { additional_context: Vec, ctx: &mut ModelContext, ) { + if self + .in_flight_response_streams + .has_active_stream_for_conversation(conversation_id, ctx) + { + // Resume is redundant while the existing generation still owns the conversation. + // Treat repeated clicks as a safe no-op instead of constructing overlapping input. + log::info!( + "Ignoring resume request for conversation {conversation_id:?}; a response is still in flight" + ); + return; + } + let Some(conversation) = BlocklistAIHistoryModel::as_ref(ctx).conversation(&conversation_id) else { @@ -5034,7 +5097,7 @@ impl BlocklistAIController { ); const AI_INPUT_NOT_SENT_ERROR_STR: &str = "Not sending AI input because there is an in-flight request"; - safe_assert!(false, "{}", AI_INPUT_NOT_SENT_ERROR_STR); + log::warn!("{AI_INPUT_NOT_SENT_ERROR_STR}"); return Err(anyhow::anyhow!(AI_INPUT_NOT_SENT_ERROR_STR)); } @@ -5818,6 +5881,7 @@ impl BlocklistAIController { root_task_id, did_input_contain_user_query, persistence_offset, + last_progressive_summary_failure_context_tokens, cancellation_reason, committed_provider_batch, finished_provider_batch, @@ -5920,6 +5984,7 @@ impl BlocklistAIController { action_context, messages_sent, persistence_offset, + last_progressive_summary_failure_context_tokens, }), checkpoint: None, turn_control: None, @@ -6315,6 +6380,7 @@ impl BlocklistAIController { action_context, messages_sent, persistence_offset, + last_progressive_summary_failure_context_tokens: None, }; if let Some(reason) = cancellation_reason { if let Err(error) = run.coordinator.run_mut().cancel(reason.to_string()) { @@ -6482,23 +6548,23 @@ impl BlocklistAIController { if model_is_usable(&terminal_model_id, &terminal_provider_config) && candidate_fits(terminal_context_limit) { - candidates.push(ProgressiveSummaryCandidate { - model_id: terminal_model_id, - context_limit: terminal_context_limit, - provider_config: terminal_provider_config, - }); + candidates.push(progressive_summary_candidate( + terminal_model_id, + terminal_context_limit, + terminal_provider_config, + )); } if model_is_usable(&base_model_id, &base_provider_config) && candidate_fits(base_context_limit) && !candidates .iter() - .any(|candidate| candidate.model_id == base_model_id) + .any(|candidate| candidate.selection_model_id == base_model_id) { - candidates.push(ProgressiveSummaryCandidate { - model_id: base_model_id, - context_limit: base_context_limit, - provider_config: base_provider_config, - }); + candidates.push(progressive_summary_candidate( + base_model_id, + base_context_limit, + base_provider_config, + )); } candidates } @@ -6508,6 +6574,12 @@ impl BlocklistAIController { conversation_id: AIConversationId, ctx: &mut ModelContext, ) -> bool { + if self + .active_provider_progressive_summaries + .contains_key(&conversation_id) + { + return false; + } let Some(active_run) = self .active_provider_runs .get(&conversation_id) @@ -6532,6 +6604,8 @@ impl BlocklistAIController { ) }) .max(1); + let last_failure_context_tokens = + active_run.last_progressive_summary_failure_context_tokens; let (current_context_tokens, summary_pending) = { let history = BlocklistAIHistoryModel::as_ref(ctx); let Some(conversation) = history.conversation(&conversation_id) else { @@ -6545,6 +6619,7 @@ impl BlocklistAIController { ) }; if summary_pending + || last_failure_context_tokens == Some(current_context_tokens) || current_context_tokens as f32 / (active_context_limit as f32) < PROGRESSIVE_SUMMARY_TRIGGER_USAGE { @@ -6583,61 +6658,51 @@ impl BlocklistAIController { let plan = ActiveProgressiveSummaryPlan { split_point, retained_messages_count: transcript.len().saturating_sub(split_point), + trigger_context_tokens: current_context_tokens, active_context_limit, persistent_context_tokens, candidates, + summarized_prefix: transcript[..split_point].to_vec(), summarize_messages: vec![ConversationMessage { role: MessageRole::User, content: MessageContent::Text(summarize_content), }], }; - let Some(slot) = self.active_provider_runs.get_mut(&conversation_id) else { + let Some(slot) = self.active_provider_runs.get(&conversation_id) else { return false; }; - let Some(run) = slot.run.take() else { - return false; - }; - let checkpoint = match ActiveProviderRunCheckpoint::from_active_run(&run) { - Ok(checkpoint) => checkpoint, - Err(error) => { - slot.run = Some(run); - self.fail_active_provider_run(conversation_id, error, ctx); - return true; - } - }; - slot.checkpoint = Some(checkpoint); - let stream_id = slot.stream_id.clone(); - let run_id = slot.run_id.clone(); let identity = ActiveProviderRunIdentity { conversation_id, - stream_id, - run_id, + stream_id: slot.stream_id.clone(), + run_id: slot.run_id.clone(), }; + self.active_provider_progressive_summaries.insert( + conversation_id, + ActiveProviderProgressiveSummaryState::InFlight { + identity: identity.clone(), + }, + ); BlocklistAIHistoryModel::handle(ctx).update(ctx, |history, _| { if let Some(conversation) = history.conversation_mut(&conversation_id) { conversation.set_has_pending_progressive_summary(true); } }); - if let Err(error) = self.persist_active_provider_run(conversation_id, ctx) { - if let Some(slot) = self.active_provider_runs.get_mut(&conversation_id) { - slot.run = Some(run); - } - self.fail_active_provider_run( - conversation_id, - format!("failed to checkpoint provider run before summarization: {error}"), - ctx, - ); - return true; - } let candidate_names = plan .candidates .iter() - .map(|candidate| format!("{}({})", candidate.model_id, candidate.context_limit)) + .map(|candidate| { + format!( + "{} -> {}({})", + candidate.selection_model_id, + candidate.request_model_id, + candidate.context_limit + ) + }) .join(", "); log::info!( - "[progressive-summary] Pausing active run {:?}: summarizing {} messages, retaining {}, candidates=[{}]", + "[progressive-summary] Started background compaction for active run {:?}: summarizing immutable prefix of {} messages, current tail {}, candidates=[{}]", conversation_id, plan.split_point, plan.retained_messages_count, @@ -6648,20 +6713,22 @@ impl BlocklistAIController { let mut errors = Vec::new(); for candidate in plan.candidates.clone() { let mut request = TurnRequest::new( - candidate.model_id.clone(), + candidate.request_model_id.clone(), plan.summarize_messages.clone(), ); request.system_prompt = Some(PROGRESSIVE_SUMMARY_PROMPT.to_string()); request.max_output_tokens = Some(u64::from(PROGRESSIVE_SUMMARY_OUTPUT_TOKENS)); match collect_progressive_summary(candidate.provider_config, request).await { Ok((summary, usage)) => { - return (run, plan, Ok((summary, usage, candidate.model_id))); + return (plan, Ok((summary, usage, candidate.request_model_id))); } - Err(error) => errors.push(format!("{}: {error}", candidate.model_id)), + Err(error) => errors.push(format!( + "{} -> {}: {error}", + candidate.selection_model_id, candidate.request_model_id + )), } } ( - run, plan, Err(anyhow!( "all progressive summary candidates failed: {}", @@ -6669,8 +6736,8 @@ impl BlocklistAIController { )), ) }, - move |me, (run, plan, result), ctx| { - me.finish_active_provider_progressive_summary(identity, run, plan, result, ctx); + move |me, (plan, result), ctx| { + me.finish_active_provider_progressive_summary(identity, plan, result, ctx); }, ); true @@ -6679,42 +6746,118 @@ impl BlocklistAIController { fn finish_active_provider_progressive_summary( &mut self, identity: ActiveProviderRunIdentity, - mut run: ActiveProviderRun, plan: ActiveProgressiveSummaryPlan, result: anyhow::Result<(String, Usage, String)>, ctx: &mut ModelContext, ) { - let ActiveProviderRunIdentity { - conversation_id, - stream_id, - run_id, - } = identity; - let Some(slot) = self.active_provider_runs.get(&conversation_id) else { + let conversation_id = identity.conversation_id; + let owns_summary = self + .active_provider_progressive_summaries + .get(&conversation_id) + .is_some_and(|state| { + matches!( + state, + ActiveProviderProgressiveSummaryState::InFlight { + identity: active_identity, + } if active_identity == &identity + ) + }); + if !owns_summary { return; - }; - if slot.stream_id != stream_id || slot.run_id != run_id { + } + let identity_is_current = + self.active_provider_runs + .get(&conversation_id) + .is_some_and(|slot| { + slot.stream_id == identity.stream_id && slot.run_id == identity.run_id + }); + if !identity_is_current { + self.active_provider_progressive_summaries + .remove(&conversation_id); + BlocklistAIHistoryModel::handle(ctx).update(ctx, |history, _| { + if let Some(conversation) = history.conversation_mut(&conversation_id) { + conversation.set_has_pending_progressive_summary(false); + } + }); return; } + self.active_provider_progressive_summaries.insert( + conversation_id, + ActiveProviderProgressiveSummaryState::Ready { + identity, + plan, + result, + }, + ); + self.drive_active_provider_run(conversation_id, ctx); + } + + fn apply_ready_active_provider_progressive_summary( + &mut self, + conversation_id: AIConversationId, + ctx: &mut ModelContext, + ) -> bool { + let can_apply = match ( + self.active_provider_progressive_summaries + .get(&conversation_id), + self.active_provider_runs.get(&conversation_id), + ) { + (Some(ActiveProviderProgressiveSummaryState::Ready { identity, .. }), Some(slot)) => { + slot.stream_id == identity.stream_id + && slot.run_id == identity.run_id + && slot.run.as_ref().is_some_and(|run| { + matches!( + run.coordinator.run().state(), + ProviderRunState::ReadyToCallModel + ) + }) + } + (Some(ActiveProviderProgressiveSummaryState::InFlight { .. }), _) + | (Some(ActiveProviderProgressiveSummaryState::Ready { .. }), None) + | (None, _) => false, + }; + if !can_apply { + return false; + } + + let Some(ActiveProviderProgressiveSummaryState::Ready { + identity: _, + plan, + result, + }) = self + .active_provider_progressive_summaries + .remove(&conversation_id) + else { + return false; + }; BlocklistAIHistoryModel::handle(ctx).update(ctx, |history, _| { if let Some(conversation) = history.conversation_mut(&conversation_id) { conversation.set_has_pending_progressive_summary(false); } }); + let mut summary_applied = false; + let mut recorded_usage = None; match result { Ok((summary, usage, summarizer_model_id)) => { + let Some(run) = self + .active_provider_runs + .get_mut(&conversation_id) + .and_then(|slot| slot.run.as_mut()) + else { + return false; + }; let transcript = run.coordinator.run().transcript(); - if plan.split_point > transcript.len() { - if let Err(error) = run.coordinator.run_mut().fail( - ProviderRunFailureKind::Protocol, - "provider transcript changed while progressive summarization was running", - ) { - log::error!("Failed to record progressive summary protocol error: {error}"); - } + if transcript.get(..plan.split_point) != Some(plan.summarized_prefix.as_slice()) { + log::warn!( + "[progressive-summary] Discarding stale background summary for active conversation {:?}; the snapshotted prefix no longer matches", + conversation_id, + ); } else { let drained = transcript[..plan.split_point].to_vec(); let retained = transcript[plan.split_point..].to_vec(); + let retained_count = retained.len(); let summary_prefix = progressive_summary_messages(summary.clone()); let summary_prefix_tokens = estimated_history_tokens(&summary_prefix); if let Err(error) = run @@ -6749,20 +6892,16 @@ impl BlocklistAIController { conversation.set_context_window_usage(new_usage.clamp(0.0, 1.0)); }); log::info!( - "[progressive-summary] Resumed active run {:?} with {}: ~{} tokens ({:.1}% of {} context), {} messages retained", + "[progressive-summary] Hot-swapped background summary into active run {:?} with {}: ~{} tokens ({:.1}% of {} context), {} tail messages preserved", conversation_id, summarizer_model_id, new_context_tokens, new_usage * 100.0, plan.active_context_limit, - plan.retained_messages_count, - ); - self.record_progressive_summary_usage( - conversation_id, - usage, - summarizer_model_id, - ctx, + retained_count, ); + recorded_usage = Some((usage, summarizer_model_id)); + summary_applied = true; } } } @@ -6774,23 +6913,34 @@ impl BlocklistAIController { } } - let Some(slot) = self.active_provider_runs.get_mut(&conversation_id) else { - return; - }; - slot.run = Some(run); - slot.checkpoint = slot - .run - .as_ref() - .and_then(|run| ActiveProviderRunCheckpoint::from_active_run(run).ok()); + let failure_context_tokens = BlocklistAIHistoryModel::as_ref(ctx) + .conversation(&conversation_id) + .map(AIConversation::current_context_tokens) + .unwrap_or(plan.trigger_context_tokens); + if let Some(run) = self + .active_provider_runs + .get_mut(&conversation_id) + .and_then(|slot| slot.run.as_mut()) + { + run.last_progressive_summary_failure_context_tokens = if summary_applied { + None + } else { + // Fail open and suppress another request at this unchanged context boundary. + Some(failure_context_tokens) + }; + } + if let Some((usage, summarizer_model_id)) = recorded_usage { + self.record_progressive_summary_usage(conversation_id, usage, summarizer_model_id, ctx); + } if let Err(error) = self.persist_active_provider_run(conversation_id, ctx) { self.fail_active_provider_run( conversation_id, format!("failed to persist provider run after summarization: {error}"), ctx, ); - return; + return true; } - self.drive_active_provider_run(conversation_id, ctx); + false } fn record_progressive_summary_usage( @@ -6833,9 +6983,10 @@ impl BlocklistAIController { conversation_id: AIConversationId, ctx: &mut ModelContext, ) { - if self.begin_active_provider_progressive_summary(conversation_id, ctx) { + if self.apply_ready_active_provider_progressive_summary(conversation_id, ctx) { return; } + self.begin_active_provider_progressive_summary(conversation_id, ctx); let Some(slot) = self.active_provider_runs.get_mut(&conversation_id) else { return; }; @@ -8104,10 +8255,13 @@ impl BlocklistAIController { } } self.active_provider_runs.remove(&conversation_id); + self.active_provider_progressive_summaries + .remove(&conversation_id); self.restoring_provider_runs.remove(&conversation_id); self.in_flight_response_streams.cleanup_stream(stream_id); BlocklistAIHistoryModel::handle(ctx).update(ctx, |history_model, _| { if let Some(conversation) = history_model.conversation_mut(&conversation_id) { + conversation.set_has_pending_progressive_summary(false); conversation.cleanup_completed_response_stream(stream_id); } }); @@ -9683,23 +9837,23 @@ impl BlocklistAIController { if model_is_usable(&terminal_model_id, &terminal_provider_config) && candidate_fits(terminal_context_limit) { - candidates.push(ProgressiveSummaryCandidate { - model_id: terminal_model_id.clone(), - context_limit: terminal_context_limit, - provider_config: terminal_provider_config, - }); + candidates.push(progressive_summary_candidate( + terminal_model_id.clone(), + terminal_context_limit, + terminal_provider_config, + )); } if model_is_usable(&base_model_id, &base_provider_config) && candidate_fits(base_context_limit) && !candidates .iter() - .any(|candidate| candidate.model_id == base_model_id) + .any(|candidate| candidate.selection_model_id == base_model_id) { - candidates.push(ProgressiveSummaryCandidate { - model_id: base_model_id.clone(), - context_limit: base_context_limit, - provider_config: base_provider_config, - }); + candidates.push(progressive_summary_candidate( + base_model_id.clone(), + base_context_limit, + base_provider_config, + )); } if candidates.is_empty() { log::warn!( @@ -9718,7 +9872,14 @@ impl BlocklistAIController { let candidate_names = candidates .iter() - .map(|candidate| format!("{}({})", candidate.model_id, candidate.context_limit)) + .map(|candidate| { + format!( + "{} -> {}({})", + candidate.selection_model_id, + candidate.request_model_id, + candidate.context_limit + ) + }) .join(", "); log::info!( "[progressive-summary] Triggering for {:?}: summarizing {} messages, retaining {}, candidates=[{}]", @@ -9737,15 +9898,20 @@ impl BlocklistAIController { async move { let mut errors = Vec::new(); for candidate in candidates { - let mut request = - TurnRequest::new(candidate.model_id.clone(), summarize_messages.clone()); + let mut request = TurnRequest::new( + candidate.request_model_id.clone(), + summarize_messages.clone(), + ); request.system_prompt = Some(summarize_prompt.to_string()); request.max_output_tokens = Some(u64::from(PROGRESSIVE_SUMMARY_OUTPUT_TOKENS)); match collect_progressive_summary(candidate.provider_config, request).await { Ok((summary, usage)) => { - return Ok((summary, usage, candidate.model_id)); + return Ok((summary, usage, candidate.request_model_id)); } - Err(error) => errors.push(format!("{}: {error}", candidate.model_id)), + Err(error) => errors.push(format!( + "{} -> {}: {error}", + candidate.selection_model_id, candidate.request_model_id + )), } } Err(anyhow!( diff --git a/app/src/ai/blocklist/controller_tests.rs b/app/src/ai/blocklist/controller_tests.rs index bf50f9a7..9637cc7b 100644 --- a/app/src/ai/blocklist/controller_tests.rs +++ b/app/src/ai/blocklist/controller_tests.rs @@ -278,6 +278,7 @@ fn provider_snapshot(conversation_id: AIConversationId) -> super::ActiveProvider root_task_id: task_id, did_input_contain_user_query: true, persistence_offset: 0, + last_progressive_summary_failure_context_tokens: None, cancellation_reason: None, committed_provider_batch: None, finished_provider_batch: None, @@ -1022,6 +1023,8 @@ fn cancelling_provider_startup_keeps_slot_and_durable_cancellation() { response_config: snapshot.response_config.clone(), action_context: snapshot.action_context.clone(), persistence_offset: snapshot.persistence_offset, + last_progressive_summary_failure_context_tokens: snapshot + .last_progressive_summary_failure_context_tokens, }; terminal.ai_controller().update(ctx, |controller, ctx| { @@ -1133,6 +1136,8 @@ fn same_conversation_follow_up_waits_for_cancelled_provider_generation_cleanup() response_config: old_snapshot.response_config.clone(), action_context: old_snapshot.action_context.clone(), persistence_offset: old_snapshot.persistence_offset, + last_progressive_summary_failure_context_tokens: old_snapshot + .last_progressive_summary_failure_context_tokens, }), turn_control: None, cancellation_reason: Some(CancellationReason::FollowUpSubmitted { diff --git a/app/src/pane_group/child_agent/mod.rs b/app/src/pane_group/child_agent/mod.rs index cb48f804..c021c491 100644 --- a/app/src/pane_group/child_agent/mod.rs +++ b/app/src/pane_group/child_agent/mod.rs @@ -95,13 +95,13 @@ fn propagate_parent_agent_settings( profiles.set_active_profile(child_terminal_view_id, parent_profile_id, ctx); }); - let parent_base_model_id = LLMPreferences::as_ref(ctx) - .get_active_base_model(ctx, Some(parent_view_id)) + let parent_terminal_model_id = LLMPreferences::as_ref(ctx) + .get_active_cli_agent_model(ctx, Some(parent_view_id)) .id .clone(); LLMPreferences::handle(ctx).update(ctx, |llm_prefs, ctx| { llm_prefs.update_preferred_agent_mode_llm( - &parent_base_model_id, + &parent_terminal_model_id, child_terminal_view_id, ctx, ); diff --git a/app/src/pane_group/pane/terminal_pane.rs b/app/src/pane_group/pane/terminal_pane.rs index 74076231..392b3708 100644 --- a/app/src/pane_group/pane/terminal_pane.rs +++ b/app/src/pane_group/pane/terminal_pane.rs @@ -122,7 +122,7 @@ fn serialize_proto_to_base64(message: &M) -> String { } /// Overrides the child's preferred agent-mode LLM. `None` is a no-op -/// (inherits the parent's LLM via `propagate_parent_agent_settings`). +/// (inherits the parent's terminal model via `propagate_parent_agent_settings`). #[cfg(not(target_family = "wasm"))] fn apply_child_model_id_override( child_terminal_view_id: EntityId,