diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs
index a526af9d22..79d2301b87 100644
--- a/crates/goose-server/src/openapi.rs
+++ b/crates/goose-server/src/openapi.rs
@@ -20,9 +20,10 @@ use goose::config::declarative_providers::{
DeclarativeProviderConfig, EnvVarConfig, LoadedProvider, ProviderEngine,
};
use goose::conversation::message::{
- ActionRequired, ActionRequiredData, FrontendToolRequest, Message, MessageContent,
- MessageMetadata, RedactedThinkingContent, SystemNotificationContent, SystemNotificationType,
- ThinkingContent, TokenState, ToolConfirmationRequest, ToolRequest, ToolResponse,
+ ActionRequired, ActionRequiredData, FrontendToolRequest, InferenceMetadata, Message,
+ MessageContent, MessageMetadata, RedactedThinkingContent, SystemNotificationContent,
+ SystemNotificationType, ThinkingContent, TokenState, ToolConfirmationRequest, ToolRequest,
+ ToolResponse,
};
use crate::routes::recipe_utils::RecipeManifest;
@@ -528,6 +529,7 @@ derive_utoipa!(IconTheme as IconThemeSchema);
Message,
MessageContent,
MessageMetadata,
+ InferenceMetadata,
TokenState,
ContentSchema,
EmbeddedResourceSchema,
diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs
index 3e4a00598d..3ac741e623 100644
--- a/crates/goose/src/agents/agent.rs
+++ b/crates/goose/src/agents/agent.rs
@@ -34,8 +34,8 @@ use crate::context_mgmt::{
check_if_compaction_needed, compact_messages, DEFAULT_COMPACTION_THRESHOLD,
};
use crate::conversation::message::{
- ActionRequiredData, Message, MessageContent, ProviderMetadata, SystemNotificationType,
- ToolRequest,
+ ActionRequiredData, InferenceMetadata, Message, MessageContent, ProviderMetadata,
+ SystemNotificationType, ToolRequest,
};
use crate::conversation::{debug_conversation_fix, fix_conversation, Conversation};
use crate::mcp_utils::ToolResult;
@@ -1586,9 +1586,22 @@ impl Agent {
self.reset_retry_attempts().await;
let provider = self.provider().await?;
+ let provider_name = provider.get_name().to_string();
+ let requested_model = provider.get_model_config().model_name;
+ let inference = provider
+ .fetch_model_info(&requested_model)
+ .await
+ .ok()
+ .and_then(|model_info| model_info.resolved_model)
+ .map(|resolved_model| InferenceMetadata {
+ provider: provider_name,
+ requested_model,
+ resolved_model: Some(resolved_model),
+ });
let session_manager = self.config.session_manager.clone();
let session_id = session_config.id.clone();
if !self.config.disable_session_naming {
+ let provider = provider.clone();
let manager_for_spawn = session_manager.clone();
let session_name_update_tx = self.config.session_name_update_tx.clone();
tokio::spawn(async move {
@@ -1726,6 +1739,17 @@ impl Agent {
)
.await;
+ let filtered_response = if let Some(inference) = inference.as_ref() {
+ filtered_response.with_inference(inference.clone())
+ } else {
+ filtered_response
+ };
+ let response = if let Some(inference) = inference.as_ref() {
+ response.with_inference(inference.clone())
+ } else {
+ response
+ };
+
surfaced_thinking_in_turn |= filtered_response.content.iter().any(
|content| {
matches!(
@@ -2234,6 +2258,16 @@ impl Agent {
}
}
+ let messages_to_add = if let Some(ref inference) = inference {
+ Conversation::new_unvalidated(
+ messages_to_add
+ .into_iter()
+ .map(|message| message.with_inference_if_assistant(inference.clone())),
+ )
+ } else {
+ messages_to_add
+ };
+
for msg in &messages_to_add {
session_manager.add_message(&session_config.id, msg).await?;
}
diff --git a/crates/goose/src/context_mgmt/mod.rs b/crates/goose/src/context_mgmt/mod.rs
index 7f3a1f9733..4d62daf8ae 100644
--- a/crates/goose/src/context_mgmt/mod.rs
+++ b/crates/goose/src/context_mgmt/mod.rs
@@ -143,7 +143,7 @@ pub async fn compact_messages(
// This is the most recent message and we're preserving it by adding a fresh copy
MessageMetadata::invisible()
} else {
- msg.metadata.with_agent_invisible()
+ msg.metadata.clone().with_agent_invisible()
};
let updated_msg = msg.clone().with_metadata(updated_metadata);
final_messages.push(updated_msg);
diff --git a/crates/goose/src/conversation/message.rs b/crates/goose/src/conversation/message.rs
index 180012f678..7f1eb5fa65 100644
--- a/crates/goose/src/conversation/message.rs
+++ b/crates/goose/src/conversation/message.rs
@@ -641,14 +641,25 @@ impl From for Message {
}
}
-#[derive(ToSchema, Clone, Copy, PartialEq, Serialize, Deserialize, Debug)]
-/// Metadata for message visibility
+#[derive(ToSchema, Clone, PartialEq, Serialize, Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct InferenceMetadata {
+ pub provider: String,
+ pub requested_model: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub resolved_model: Option,
+}
+
+#[derive(ToSchema, Clone, PartialEq, Serialize, Deserialize, Debug)]
+/// Metadata for message visibility and model inference details
#[serde(rename_all = "camelCase")]
pub struct MessageMetadata {
/// Whether the message should be visible to the user in the UI
pub user_visible: bool,
/// Whether the message should be included in the agent's context window
pub agent_visible: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub inference: Option,
}
impl Default for MessageMetadata {
@@ -656,6 +667,7 @@ impl Default for MessageMetadata {
MessageMetadata {
user_visible: true,
agent_visible: true,
+ inference: None,
}
}
}
@@ -666,6 +678,7 @@ impl MessageMetadata {
MessageMetadata {
user_visible: false,
agent_visible: true,
+ ..Default::default()
}
}
@@ -674,6 +687,7 @@ impl MessageMetadata {
MessageMetadata {
user_visible: true,
agent_visible: false,
+ ..Default::default()
}
}
@@ -682,6 +696,7 @@ impl MessageMetadata {
MessageMetadata {
user_visible: false,
agent_visible: false,
+ ..Default::default()
}
}
@@ -716,6 +731,11 @@ impl MessageMetadata {
..self
}
}
+
+ pub fn with_inference(mut self, inference: InferenceMetadata) -> Self {
+ self.inference = Some(inference);
+ self
+ }
}
#[derive(ToSchema, Clone, PartialEq, Serialize, Deserialize, Debug)]
@@ -996,6 +1016,19 @@ impl Message {
self
}
+ pub fn with_inference(mut self, inference: InferenceMetadata) -> Self {
+ self.metadata = self.metadata.with_inference(inference);
+ self
+ }
+
+ pub fn with_inference_if_assistant(self, inference: InferenceMetadata) -> Self {
+ if self.role == Role::Assistant && self.metadata.inference.is_none() {
+ self.with_inference(inference)
+ } else {
+ self
+ }
+ }
+
pub fn user_only(mut self) -> Self {
self.metadata.user_visible = true;
self.metadata.agent_visible = false;
diff --git a/crates/goose/src/conversation/mod.rs b/crates/goose/src/conversation/mod.rs
index 2b65bbf792..31d3f52d61 100644
--- a/crates/goose/src/conversation/mod.rs
+++ b/crates/goose/src/conversation/mod.rs
@@ -43,11 +43,26 @@ impl Conversation {
}
pub fn push(&mut self, message: Message) {
+ if message.content.is_empty() && message.metadata.inference.is_some() {
+ if let Some(existing) = self
+ .0
+ .iter_mut()
+ .rev()
+ .find(|m| m.role == message.role && m.is_user_visible())
+ {
+ existing.metadata.inference = message.metadata.inference;
+ }
+ return;
+ }
+
if let Some(last) = self
.0
.last_mut()
.filter(|m| m.id.is_some() && m.id == message.id)
{
+ if message.metadata.inference.is_some() {
+ last.metadata.inference = message.metadata.inference.clone();
+ }
match (last.content.last_mut(), message.content.last()) {
(Some(MessageContent::Text(ref mut last)), Some(MessageContent::Text(new)))
if message.content.len() == 1 =>
diff --git a/crates/goose/src/providers/base.rs b/crates/goose/src/providers/base.rs
index 2d6bdf3def..0737cbed6b 100644
--- a/crates/goose/src/providers/base.rs
+++ b/crates/goose/src/providers/base.rs
@@ -385,6 +385,9 @@ pub static MSG_COUNT_FOR_SESSION_NAME_GENERATION: usize = 3;
pub struct ModelInfo {
/// The name of the model
pub name: String,
+ /// The underlying model resolved from provider metadata, when the configured model is an alias or endpoint.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub resolved_model: Option,
/// The maximum context length this model supports
pub context_limit: usize,
/// Cost per token for input in USD (optional)
@@ -405,6 +408,7 @@ impl ModelInfo {
pub fn new(name: impl Into, context_limit: usize) -> Self {
Self {
name: name.into(),
+ resolved_model: None,
context_limit,
input_token_cost: None,
output_token_cost: None,
@@ -423,6 +427,7 @@ impl ModelInfo {
) -> Self {
Self {
name: name.into(),
+ resolved_model: None,
context_limit,
input_token_cost: Some(input_cost),
output_token_cost: Some(output_cost),
@@ -448,6 +453,7 @@ fn model_info_for_provider_model(provider_name: &str, model_name: &str) -> Model
ModelInfo {
name: model_name.to_string(),
+ resolved_model: None,
context_limit: ModelConfig::new_or_fail(model_name)
.with_canonical_limits(provider_name)
.context_limit(),
@@ -1778,6 +1784,7 @@ mod tests {
// Test direct ModelInfo creation
let info = ModelInfo {
name: "test-model".to_string(),
+ resolved_model: None,
context_limit: 1000,
input_token_cost: None,
output_token_cost: None,
@@ -1790,6 +1797,7 @@ mod tests {
// Test equality
let info2 = ModelInfo {
name: "test-model".to_string(),
+ resolved_model: None,
context_limit: 1000,
input_token_cost: None,
output_token_cost: None,
@@ -1802,6 +1810,7 @@ mod tests {
// Test inequality
let info3 = ModelInfo {
name: "test-model".to_string(),
+ resolved_model: None,
context_limit: 2000,
input_token_cost: None,
output_token_cost: None,
diff --git a/crates/goose/src/providers/databricks.rs b/crates/goose/src/providers/databricks.rs
index a7f512e16f..b1276e5f19 100644
--- a/crates/goose/src/providers/databricks.rs
+++ b/crates/goose/src/providers/databricks.rs
@@ -539,6 +539,7 @@ impl DatabricksProvider {
ModelInfo {
name: info.name,
+ resolved_model: info.upstream_model_name,
context_limit,
input_token_cost: None,
output_token_cost: None,
@@ -999,6 +1000,14 @@ mod tests {
assert_eq!(info.name, "goose");
assert_eq!(info.upstream_model_name.as_deref(), Some("claude-opus-4.6"));
assert_eq!(info.reasoning, Some(true));
+
+ let model_info = DatabricksProvider::model_info_from_endpoint(info);
+ assert_eq!(model_info.name, "goose");
+ assert_eq!(
+ model_info.resolved_model.as_deref(),
+ Some("claude-opus-4.6")
+ );
+ assert!(model_info.reasoning);
}
#[test]
diff --git a/crates/goose/src/providers/formats/openai.rs b/crates/goose/src/providers/formats/openai.rs
index 1ec5aa1308..b9d7ba285d 100644
--- a/crates/goose/src/providers/formats/openai.rs
+++ b/crates/goose/src/providers/formats/openai.rs
@@ -734,15 +734,22 @@ pub fn get_usage(usage: &Value) -> Usage {
.with_cache_tokens(cache_read_input_tokens, cache_write_input_tokens)
}
-fn extract_usage_with_output_tokens(chunk: &StreamingChunk) -> Option {
+fn extract_usage_with_output_tokens(
+ chunk: &StreamingChunk,
+ fallback_model: Option<&str>,
+) -> Option {
chunk
.usage
.as_ref()
.and_then(|u| {
- chunk.model.as_ref().map(|model| ProviderUsage {
- usage: get_usage(u),
- model: model.clone(),
- })
+ chunk
+ .model
+ .as_deref()
+ .or(fallback_model)
+ .map(|model| ProviderUsage {
+ usage: get_usage(u),
+ model: model.to_string(),
+ })
})
.filter(|u| u.usage.output_tokens.is_some())
}
@@ -901,6 +908,7 @@ where
// reasoning will arrive. Emitting it immediately and then receiving
// reasoning_content in a later chunk would produce duplicated reasoning.
let mut pending_inline_thinking = String::new();
+ let mut last_seen_model: Option = None;
'outer: while let Some(response) = stream.next().await {
let response_str = response?;
@@ -917,6 +925,9 @@ where
let chunk: StreamingChunk = parse_streaming_chunk(
line.ok_or_else(|| anyhow!("unexpected stream format"))?
)?;
+ if let Some(model) = &chunk.model {
+ last_seen_model = Some(model.clone());
+ }
if !chunk.choices.is_empty() {
if let Some(details) = &chunk.choices[0].delta.reasoning_details {
@@ -931,7 +942,7 @@ where
}
}
- let mut usage = extract_usage_with_output_tokens(&chunk);
+ let mut usage = extract_usage_with_output_tokens(&chunk, last_seen_model.as_deref());
if chunk.choices.is_empty() {
yield (None, usage)
@@ -959,8 +970,11 @@ where
}
let tool_chunk: StreamingChunk = parse_streaming_chunk(line)?;
+ if let Some(model) = &tool_chunk.model {
+ last_seen_model = Some(model.clone());
+ }
- if let Some(chunk_usage) = extract_usage_with_output_tokens(&tool_chunk) {
+ if let Some(chunk_usage) = extract_usage_with_output_tokens(&tool_chunk, last_seen_model.as_deref()) {
usage = Some(chunk_usage);
}
@@ -2528,6 +2542,10 @@ data: [DONE]
assert_eq!(result.tool_calls.len(), 1, "Expected 1 tool call");
assert_eq!(result.tool_calls[0], "developer__shell");
assert_usage_yielded_once(&result, 8320, 172, 8492);
+ assert_eq!(
+ result.usage.as_ref().map(|usage| usage.model.as_str()),
+ Some("gpt-5.2-1106-preview")
+ );
Ok(())
}
diff --git a/crates/goose/src/providers/provider_registry.rs b/crates/goose/src/providers/provider_registry.rs
index ee4b6a1323..1970da7fe3 100644
--- a/crates/goose/src/providers/provider_registry.rs
+++ b/crates/goose/src/providers/provider_registry.rs
@@ -177,6 +177,7 @@ impl ProviderRegistry {
.iter()
.map(|m| ModelInfo {
name: m.name.clone(),
+ resolved_model: None,
context_limit: m.context_limit,
input_token_cost: m.input_token_cost,
output_token_cost: m.output_token_cost,
diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json
index 98be0e380a..7cebf3353e 100644
--- a/ui/desktop/openapi.json
+++ b/ui/desktop/openapi.json
@@ -5799,6 +5799,25 @@
}
}
},
+ "InferenceMetadata": {
+ "type": "object",
+ "required": [
+ "provider",
+ "requestedModel"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "requestedModel": {
+ "type": "string"
+ },
+ "resolvedModel": {
+ "type": "string",
+ "nullable": true
+ }
+ }
+ },
"InspectJobResponse": {
"type": "object",
"properties": {
@@ -6394,7 +6413,7 @@
},
"MessageMetadata": {
"type": "object",
- "description": "Metadata for message visibility",
+ "description": "Metadata for message visibility and model inference details",
"required": [
"userVisible",
"agentVisible"
@@ -6404,6 +6423,14 @@
"type": "boolean",
"description": "Whether the message should be included in the agent's context window"
},
+ "inference": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/InferenceMetadata"
+ }
+ ],
+ "nullable": true
+ },
"userVisible": {
"type": "boolean",
"description": "Whether the message should be visible to the user in the UI"
@@ -6587,6 +6614,11 @@
"type": "boolean",
"description": "Whether this model supports reasoning/thinking controls"
},
+ "resolved_model": {
+ "type": "string",
+ "description": "The underlying model resolved from provider metadata, when the configured model is an alias or endpoint.",
+ "nullable": true
+ },
"supports_cache_control": {
"type": "boolean",
"description": "Whether this model supports cache control",
diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts
index d8f51b7cc8..d42bc29fc7 100644
--- a/ui/desktop/src/api/index.ts
+++ b/ui/desktop/src/api/index.ts
@@ -1,4 +1,4 @@
// This file is auto-generated by @hey-api/openapi-ts
export { addExtension, agentAddExtension, agentRemoveExtension, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, cleanupProviderCache, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getFeatures, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModelInfo, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, importSessionNostr, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionCancel, sessionEvents, sessionReply, sessionsHandler, setConfigProvider, setRecipeSlashCommand, shareSessionNostr, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, syncFeaturedModels, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen';
-export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponse, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProviderModelInfoQuery, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, ThinkingEffort, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen';
+export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponse, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InferenceMetadata, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProviderModelInfoQuery, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, ThinkingEffort, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen';
diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts
index f9ad4a5061..b5535a239f 100644
--- a/ui/desktop/src/api/types.gen.ts
+++ b/ui/desktop/src/api/types.gen.ts
@@ -602,6 +602,12 @@ export type ImportSessionRequest = {
json: string;
};
+export type InferenceMetadata = {
+ provider: string;
+ requestedModel: string;
+ resolvedModel?: string | null;
+};
+
export type InspectJobResponse = {
processStartTime?: string | null;
runningDurationSeconds?: number | null;
@@ -746,13 +752,14 @@ export type MessageEvent = {
};
/**
- * Metadata for message visibility
+ * Metadata for message visibility and model inference details
*/
export type MessageMetadata = {
/**
* Whether the message should be included in the agent's context window
*/
agentVisible: boolean;
+ inference?: InferenceMetadata | null;
/**
* Whether the message should be visible to the user in the UI
*/
@@ -822,6 +829,10 @@ export type ModelInfo = {
* Whether this model supports reasoning/thinking controls
*/
reasoning?: boolean;
+ /**
+ * The underlying model resolved from provider metadata, when the configured model is an alias or endpoint.
+ */
+ resolved_model?: string | null;
/**
* Whether this model supports cache control
*/
diff --git a/ui/desktop/src/components/BaseChat.tsx b/ui/desktop/src/components/BaseChat.tsx
index 1da20b8cdd..c973507d31 100644
--- a/ui/desktop/src/components/BaseChat.tsx
+++ b/ui/desktop/src/components/BaseChat.tsx
@@ -196,6 +196,15 @@ export default function BaseChat({
const sessionModel = session?.model_config?.model_name ?? null;
const sessionProvider = session?.provider_name ?? null;
const sessionLoaded = session !== undefined;
+ const latestInference = useMemo(() => {
+ for (let i = messages.length - 1; i >= 0; i--) {
+ const message = messages[i];
+ if (message.role === 'assistant' && message.metadata.userVisible && message.metadata.inference) {
+ return message.metadata.inference;
+ }
+ }
+ return null;
+ }, [messages]);
useEffect(() => {
if (!recipe || !isActiveSession) return;
@@ -508,6 +517,7 @@ export default function BaseChat({
sessionModel={sessionModel}
sessionProvider={sessionProvider}
sessionLoaded={sessionLoaded}
+ latestInference={latestInference}
{...customChatInputProps}
/>
diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx
index e822d011c7..e75d5b8e97 100644
--- a/ui/desktop/src/components/ChatInput.tsx
+++ b/ui/desktop/src/components/ChatInput.tsx
@@ -204,6 +204,7 @@ interface ChatInputProps {
sessionModel?: string | null;
sessionProvider?: string | null;
sessionLoaded?: boolean;
+ latestInference?: Message['metadata']['inference'] | null;
}
export default function ChatInput({
@@ -234,6 +235,7 @@ export default function ChatInput({
sessionModel,
sessionProvider,
sessionLoaded,
+ latestInference,
}: ChatInputProps) {
const [_value, setValue] = useState(initialValue);
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
@@ -1757,6 +1759,7 @@ export default function ChatInput({
setView={setView}
sessionModel={effectiveModel}
sessionProvider={effectiveProvider}
+ latestInference={latestInference}
onModelChanged={setModelOverride}
sessionLoaded={sessionLoaded}
/>
diff --git a/ui/desktop/src/components/ProgressiveMessageList.tsx b/ui/desktop/src/components/ProgressiveMessageList.tsx
index 6a492efdf8..af596a6068 100644
--- a/ui/desktop/src/components/ProgressiveMessageList.tsx
+++ b/ui/desktop/src/components/ProgressiveMessageList.tsx
@@ -14,7 +14,7 @@
* - Configurable batch size and delay
*/
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { defineMessages, useIntl } from '../i18n';
import { Message, SystemNotificationContent } from '../api';
import GooseMessage from './GooseMessage';
@@ -31,6 +31,7 @@ import { NotificationEvent } from '../types/message';
import LoadingGoose from './LoadingGoose';
import { ChatType } from '../types/chat';
import { identifyConsecutiveToolCalls, isInChain } from '../utils/toolCallChaining';
+import { getModelDisplayName } from './settings/models/predefinedModelsUtils';
const i18n = defineMessages({
loadingMessages: {
@@ -41,6 +42,10 @@ const i18n = defineMessages({
id: 'progressiveMessageList.searchHint',
defaultMessage: 'Press Cmd/Ctrl+F to load all messages immediately for search',
},
+ modelChanged: {
+ id: 'progressiveMessageList.modelChanged',
+ defaultMessage: 'Model changed: {previousModel} → {currentModel}',
+ },
});
interface ProgressiveMessageListProps {
@@ -91,6 +96,37 @@ export default function ProgressiveMessageList({
const hasOnlyToolResponses = (message: Message) =>
message.content.every((c) => c.type === 'toolResponse');
+ const getResolvedModel = useCallback((message: Message): string | null => {
+ if (message.role !== 'assistant' || !message.metadata.userVisible) return null;
+ return message.metadata.inference?.resolvedModel ?? null;
+ }, []);
+
+ const getPreviousResolvedModel = useCallback(
+ (index: number): string | null => {
+ for (let i = index - 1; i >= 0; i--) {
+ const model = getResolvedModel(messages[i]);
+ if (model) return model;
+ }
+ return null;
+ },
+ [getResolvedModel, messages]
+ );
+
+ const renderModelChangeDisclosure = useCallback(
+ (previousModel: string, currentModel: string) => (
+
+ ),
+ [intl]
+ );
+
const getSystemNotification = (message: Message): SystemNotificationContent | undefined => {
return getCreditsExhaustedNotification(message) ?? getInlineSystemNotification(message);
};
@@ -231,34 +267,46 @@ export default function ProgressiveMessageList({
const isUser = isUserMessage(message);
const messageIsInChain = isInChain(index, toolCallChains);
+ const currentResolvedModel = getResolvedModel(message);
+ const previousResolvedModel = currentResolvedModel ? getPreviousResolvedModel(index) : null;
+ const showModelChangeDisclosure = Boolean(
+ currentResolvedModel &&
+ previousResolvedModel &&
+ currentResolvedModel !== previousResolvedModel
+ );
+
+ const messageKey = message.id ?? `msg-${index}-${message.created}`;
return (
-
- {isUser ? (
- !hasOnlyToolResponses(message) && (
-
- )
- ) : (
-
- )}
-
+
+ {showModelChangeDisclosure && currentResolvedModel && previousResolvedModel &&
+ renderModelChangeDisclosure(previousResolvedModel, currentResolvedModel)}
+
+ {isUser ? (
+ !hasOnlyToolResponses(message) && (
+
+ )
+ ) : (
+
+ )}
+
+
);
})
.filter(Boolean);
@@ -274,6 +322,9 @@ export default function ProgressiveMessageList({
onMessageUpdate,
toolCallChains,
submitElicitationResponse,
+ getPreviousResolvedModel,
+ getResolvedModel,
+ renderModelChangeDisclosure,
]);
return (
diff --git a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx
index 40bbde729a..8aac4cdc63 100644
--- a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx
+++ b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx
@@ -1,5 +1,5 @@
import { Sliders, Bot, LoaderCircle, Settings } from 'lucide-react';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import { useModelAndProvider } from '../../../ModelAndProviderContext';
import { SwitchModelModal } from '../subcomponents/SwitchModelModal';
import { View } from '../../../../utils/navigationUtils';
@@ -16,6 +16,7 @@ import { getModelDisplayName } from '../predefinedModelsUtils';
import { ModelSettingsPanel } from '../../localInference/ModelSettingsPanel';
import { ScrollArea } from '../../../ui/scroll-area';
import { defineMessages, useIntl } from '../../../../i18n';
+import type { Message } from '../../../../api';
const i18n = defineMessages({
selectModel: {
@@ -42,6 +43,10 @@ const i18n = defineMessages({
id: 'modelsBottomBar.localModelSettingsTitle',
defaultMessage: 'Local Model Settings — {modelName}',
},
+ resolvedModel: {
+ id: 'modelsBottomBar.resolvedModel',
+ defaultMessage: 'Resolved model',
+ },
});
interface ModelsBottomBarProps {
@@ -50,6 +55,7 @@ interface ModelsBottomBarProps {
setView: (view: View) => void;
sessionModel?: string | null;
sessionProvider?: string | null;
+ latestInference?: Message['metadata']['inference'] | null;
onModelChanged: (override: { model: string; provider: string }) => void;
sessionLoaded?: boolean;
}
@@ -60,6 +66,7 @@ export default function ModelsBottomBar({
setView,
sessionModel,
sessionProvider,
+ latestInference,
onModelChanged,
sessionLoaded,
}: ModelsBottomBarProps) {
@@ -81,6 +88,14 @@ export default function ModelsBottomBar({
// rather than flashing the config default or leaving the footer blank.
const isModelLoading = Boolean(sessionId && !sessionLoaded);
const displayModel = currentModel || providerDefaultModel || displayModelName;
+ const resolvedModel = latestInference?.resolvedModel ?? null;
+ const shouldShowResolvedModel = Boolean(
+ !isModelLoading &&
+ resolvedModel &&
+ latestInference?.provider === currentProvider &&
+ latestInference?.requestedModel === currentModel &&
+ resolvedModel !== currentModel
+ );
const loadingModelLabel = intl.formatMessage(i18n.loadingModel);
const triggerLabel = isModelLoading ? loadingModelLabel : displayModel;
const menuModelLabel = isModelLoading ? loadingModelLabel : displayModelName;
@@ -118,6 +133,11 @@ export default function ModelsBottomBar({
setDisplayModelName(getModelDisplayName(currentModel));
}, [currentModel]);
+ const resolvedDisplayModelName = useMemo(
+ () => (resolvedModel ? getModelDisplayName(resolvedModel) : null),
+ [resolvedModel]
+ );
+
const handleModelSelected = (model: string, provider: string) => {
onModelChanged({ model, provider });
};
@@ -147,6 +167,14 @@ export default function ModelsBottomBar({
{menuModelLabel}
{!isModelLoading && displayProvider && ` — ${displayProvider}`}
+ {shouldShowResolvedModel && resolvedDisplayModelName && (
+
+
{intl.formatMessage(i18n.resolvedModel)}
+
+ {resolvedDisplayModelName}
+
+
+ )}
setIsAddModelModalOpen(true)}>
{intl.formatMessage(i18n.changeModel)}
diff --git a/ui/desktop/src/hooks/useChatStream.ts b/ui/desktop/src/hooks/useChatStream.ts
index 8895aff303..507400c236 100644
--- a/ui/desktop/src/hooks/useChatStream.ts
+++ b/ui/desktop/src/hooks/useChatStream.ts
@@ -183,6 +183,13 @@ function pushMessage(currentMessages: Message[], incomingMsg: Message): Message[
const lastContent = lastMsg.content[lastMsg.content.length - 1];
const newContent = incomingMsg.content[incomingMsg.content.length - 1];
+ if (incomingMsg.metadata?.inference) {
+ lastMsg.metadata = {
+ ...lastMsg.metadata,
+ inference: incomingMsg.metadata.inference,
+ };
+ }
+
if (
lastContent?.type === 'text' &&
newContent?.type === 'text' &&
@@ -236,6 +243,7 @@ function createEventProcessor(
let latestChatState: ChatState = ChatState.Streaming;
let lastBatchUpdate = Date.now();
let hasPendingUpdate = false;
+ let pendingInference: Message['metadata']['inference'] | undefined;
const flushBatchedUpdates = () => {
if (reduceMotion && hasPendingUpdate) {
@@ -271,12 +279,54 @@ function createEventProcessor(
}
};
+ const flushPendingInference = () => {
+ if (!pendingInference) {
+ return;
+ }
+
+ for (let i = currentMessages.length - 1; i >= 0; i--) {
+ const message = currentMessages[i];
+ if (message.role === 'assistant' && message.metadata.userVisible) {
+ currentMessages = [
+ ...currentMessages.slice(0, i),
+ {
+ ...message,
+ metadata: {
+ ...message.metadata,
+ inference: message.metadata.inference ?? pendingInference,
+ },
+ },
+ ...currentMessages.slice(i + 1),
+ ];
+ break;
+ }
+ }
+ pendingInference = undefined;
+ };
+
// Returns true if the event is terminal (Finish or Error)
const processEvent = (event: SessionEvent): boolean => {
switch (event.type) {
case 'Message': {
- const msg = (event as Record).message as Message;
+ let msg = (event as Record).message as Message;
const tokenState = (event as Record).token_state as TokenState;
+
+ if (msg.content.length === 0 && msg.metadata?.inference) {
+ pendingInference = msg.metadata.inference;
+ return false;
+ }
+
+ if (pendingInference && msg.role === 'assistant' && msg.metadata.userVisible) {
+ msg = {
+ ...msg,
+ metadata: {
+ ...msg.metadata,
+ inference: msg.metadata.inference ?? pendingInference,
+ },
+ };
+ pendingInference = undefined;
+ }
+
currentMessages = pushMessage(currentMessages, msg);
const hasToolConfirmation = msg.content.some(
@@ -301,7 +351,9 @@ function createEventProcessor(
return false;
}
case 'Error': {
+ flushPendingInference();
flushBatchedUpdates();
+ dispatch({ type: 'SET_MESSAGES', payload: currentMessages });
const errorMsg = String((event as Record).error ?? '');
if (errorMsg.includes('too far behind') && onReloadNeeded) {
// Server indicated we missed events — end streaming without setting
@@ -315,7 +367,9 @@ function createEventProcessor(
return true;
}
case 'Finish': {
+ flushPendingInference();
flushBatchedUpdates();
+ dispatch({ type: 'SET_MESSAGES', payload: currentMessages });
onFinish();
return true;
}
diff --git a/ui/desktop/src/i18n/messages/en.json b/ui/desktop/src/i18n/messages/en.json
index 6a3f5e7952..50f1406e49 100644
--- a/ui/desktop/src/i18n/messages/en.json
+++ b/ui/desktop/src/i18n/messages/en.json
@@ -2390,6 +2390,9 @@
"modelsBottomBar.localModelSettingsTitle": {
"defaultMessage": "Local Model Settings — {modelName}"
},
+ "modelsBottomBar.resolvedModel": {
+ "defaultMessage": "Resolved model"
+ },
"modelsBottomBar.selectModel": {
"defaultMessage": "Select Model"
},
@@ -2687,6 +2690,9 @@
"progressiveMessageList.loadingMessages": {
"defaultMessage": "Loading messages... ({renderedCount}/{totalCount})"
},
+ "progressiveMessageList.modelChanged": {
+ "defaultMessage": "Model changed: {previousModel} → {currentModel}"
+ },
"progressiveMessageList.searchHint": {
"defaultMessage": "Press Cmd/Ctrl+F to load all messages immediately for search"
},
diff --git a/ui/desktop/src/i18n/messages/zh-CN.json b/ui/desktop/src/i18n/messages/zh-CN.json
index a6e1239f4d..c3615acdd0 100644
--- a/ui/desktop/src/i18n/messages/zh-CN.json
+++ b/ui/desktop/src/i18n/messages/zh-CN.json
@@ -2333,6 +2333,9 @@
"modelsBottomBar.localModelSettingsTitle": {
"defaultMessage": "本地模型设置 — {modelName}"
},
+ "modelsBottomBar.resolvedModel": {
+ "defaultMessage": "解析后的模型"
+ },
"modelsBottomBar.selectModel": {
"defaultMessage": "选择模型"
},
@@ -2630,6 +2633,9 @@
"progressiveMessageList.loadingMessages": {
"defaultMessage": "正在加载消息…({renderedCount}/{totalCount})"
},
+ "progressiveMessageList.modelChanged": {
+ "defaultMessage": "模型已更改:{previousModel} → {currentModel}"
+ },
"progressiveMessageList.searchHint": {
"defaultMessage": "按 Cmd/Ctrl+F 立即加载全部消息以便搜索"
},