fix(tkmind): restore provider and session recipe on compat reply paths
Unused Dependencies / machete (push) Has been cancelled

Add POST /agent/update_from_session to apply session recipe to the agent,
and restore provider from session when session_reply finds no provider set.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-10 12:34:54 +08:00
parent 30dd3a4d2b
commit 6648c4bfbc
2 changed files with 102 additions and 0 deletions
+84
View File
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
@@ -14,6 +15,7 @@ use serde_json::Value;
use tokio_util::sync::CancellationToken;
use tracing::error;
use crate::agents::Agent;
use crate::agents::extension::ToolInfo;
use crate::agents::extension_manager::get_parameter_names;
use crate::agents::reply_parts::is_tool_visible_to_app;
@@ -25,6 +27,8 @@ use crate::config::permission::PermissionLevel;
use crate::config::resolve_extensions_for_new_session;
use crate::config::{Config, GooseMode};
use crate::providers::create;
use crate::recipe::build_recipe::{build_recipe_from_template, RecipeError};
use crate::recipe::local_recipes::get_recipe_library_dir;
use crate::recipe::Recipe;
use crate::session::session_manager::SessionType;
use crate::session::{EnabledExtensionsState, ExtensionState, Session};
@@ -71,6 +75,11 @@ pub struct UpdateWorkingDirRequest {
working_dir: String,
}
#[derive(Deserialize)]
pub struct UpdateFromSessionRequest {
session_id: String,
}
#[derive(Deserialize)]
pub struct RestartAgentRequest {
session_id: String,
@@ -658,6 +667,80 @@ async fn resume_agent(
}))
}
async fn build_recipe_with_parameter_values(
original_recipe: &Recipe,
user_recipe_values: HashMap<String, String>,
) -> Result<Option<Recipe>, String> {
let recipe_content = original_recipe
.to_yaml()
.map_err(|err| format!("Failed to serialize recipe: {err}"))?;
let recipe_dir = get_recipe_library_dir(true);
let params: Vec<(String, String)> = user_recipe_values.into_iter().collect();
match build_recipe_from_template(
recipe_content,
&recipe_dir,
params,
None::<fn(&str, &str) -> Result<String, anyhow::Error>>,
) {
Ok(recipe) => Ok(Some(recipe)),
Err(RecipeError::MissingParams { .. }) => Ok(None),
Err(err) => Err(err.to_string()),
}
}
async fn apply_recipe_to_agent(agent: &Arc<Agent>, recipe: &Recipe) {
let _ = agent
.apply_recipe_components(recipe.response.clone(), true)
.await;
if let Some(instructions) = recipe.instructions.as_ref() {
agent
.extend_system_prompt("recipe".to_string(), instructions.clone())
.await;
}
}
async fn apply_session_recipe_to_agent(
agent: &Arc<Agent>,
session: &Session,
) -> Result<(), String> {
let Some(recipe) = session.recipe.as_ref() else {
return Ok(());
};
if session.session_type == SessionType::Scheduled {
apply_recipe_to_agent(agent, recipe).await;
return Ok(());
}
let user_values = session.user_recipe_values.clone().unwrap_or_default();
match build_recipe_with_parameter_values(recipe, user_values).await? {
Some(rendered) => apply_recipe_to_agent(agent, &rendered).await,
None => {}
}
Ok(())
}
async fn update_from_session(
State(state): State<Arc<AppState>>,
Json(payload): Json<UpdateFromSessionRequest>,
) -> Result<StatusCode, (StatusCode, String)> {
let agent = state
.get_agent_for_route(payload.session_id.clone())
.await
.map_err(|status| (status, "No agent for session id".to_owned()))?;
let session = state
.session_manager()
.get_session(&payload.session_id, false)
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to get session: {err}")))?;
apply_session_recipe_to_agent(&agent, &session)
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err))?;
Ok(StatusCode::OK)
}
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/agent/start", post(start_agent))
@@ -665,6 +748,7 @@ pub fn routes(state: Arc<AppState>) -> Router {
.route("/agent/call_tool", post(call_tool))
.route("/agent/tools", get(get_tools))
.route("/agent/update_provider", post(update_agent_provider))
.route("/agent/update_from_session", post(update_from_session))
.route("/agent/update_session", post(update_session))
.route("/agent/add_extension", post(add_extension))
.route("/agent/remove_extension", post(remove_extension))
@@ -347,6 +347,24 @@ pub async fn session_reply(
}
};
if agent.provider().await.is_err() {
if let Err(error) = agent.restore_provider_from_session(&session).await {
tracing::error!(
"Failed to restore provider for session {}: {}",
task_session_id,
error
);
publish(
Some(task_request_id.clone()),
MessageEvent::Error {
error: format!("Provider not set: {}", error),
},
)
.await;
return;
}
}
let session_config = SessionConfig {
id: task_session_id.clone(),
schedule_id: session.schedule_id.clone(),