From 5fa6ffb81db7f1eafc4450c9b12d8fb30fea2064 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 1 Jun 2026 15:19:59 -0600 Subject: [PATCH] feat: Added support for auto_unload skills during chat --- src/config/request_context.rs | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/config/request_context.rs b/src/config/request_context.rs index e03be25..738315d 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -822,6 +822,7 @@ impl RequestContext { if !app.dry_run { self.save_message(app, input, output)?; } + self.skill_registry.sweep_auto_unload(); Ok(()) } @@ -3515,6 +3516,58 @@ mod tests { assert!(lm.continuous); } + #[test] + fn after_chat_completion_sweeps_auto_unload_skills_at_turn_end() { + let mut ctx = create_test_ctx(); + ctx.app = Arc::new(AppState { + config: Arc::new(AppConfig { + dry_run: true, + ..(*ctx.app.config).clone() + }), + ..(*ctx.app).clone() + }); + + let ephemeral = Skill::new("ephemeral", "---\nauto_unload: true\n---\nbody"); + let persistent = Skill::new("persistent", "---\nauto_unload: false\n---\nbody"); + ctx.skill_registry.insert(ephemeral).unwrap(); + ctx.skill_registry.insert(persistent).unwrap(); + + let input = Input::from_str(&ctx, "hello", None); + let app = Arc::clone(&ctx.app.config); + ctx.after_chat_completion(app.as_ref(), &input, "response", &[]).unwrap(); + + assert!(!ctx.skill_registry.is_loaded("ephemeral")); + assert!(ctx.skill_registry.is_loaded("persistent")); + } + + #[test] + fn after_chat_completion_preserves_auto_unload_during_tool_loop() { + let mut ctx = create_test_ctx(); + ctx.app = Arc::new(AppState { + config: Arc::new(AppConfig { + dry_run: true, + ..(*ctx.app.config).clone() + }), + ..(*ctx.app).clone() + }); + + let ephemeral = Skill::new("ephemeral", "---\nauto_unload: true\n---\nbody"); + ctx.skill_registry.insert(ephemeral).unwrap(); + + let input = Input::from_str(&ctx, "hello", None); + let app = Arc::clone(&ctx.app.config); + let tool_result = ToolResult::new( + crate::function::ToolCall::default(), + serde_json::json!({}), + ); + ctx.after_chat_completion(app.as_ref(), &input, "", &[tool_result]).unwrap(); + + assert!( + ctx.skill_registry.is_loaded("ephemeral"), + "auto_unload skills must persist through tool-using rounds" + ); + } + #[test] fn role_like_mut_returns_none_when_empty() { let mut ctx = create_test_ctx();