feat: route staged scene skills through runtime
Add registry-driven scene routing and multi-root skill loading so fault-details and 95598 scene skills can be triggered from natural language while still running through the browser-backed runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,8 @@ use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use sgclaw::compat::config_adapter::{
|
||||
build_zeroclaw_config, build_zeroclaw_config_from_settings,
|
||||
build_zeroclaw_config_from_sgclaw_settings, resolve_skills_dir, zeroclaw_default_skills_dir,
|
||||
zeroclaw_workspace_dir,
|
||||
build_zeroclaw_config_from_sgclaw_settings, resolve_scene_skills_dir_path, resolve_skills_dir,
|
||||
zeroclaw_default_skills_dir, zeroclaw_workspace_dir,
|
||||
};
|
||||
use sgclaw::config::{
|
||||
BrowserBackend, DeepSeekSettings, OfficeBackend, PlannerMode, SgClawSettings, SkillsPromptMode,
|
||||
@@ -47,7 +47,7 @@ fn zeroclaw_config_adapter_uses_deterministic_workspace_dir() {
|
||||
api_key: "key".to_string(),
|
||||
base_url: "https://proxy.example.com/v1".to_string(),
|
||||
model: "deepseek-reasoner".to_string(),
|
||||
skills_dir: None,
|
||||
skills_dir: Vec::new(),
|
||||
};
|
||||
|
||||
let workspace_dir = zeroclaw_workspace_dir(Path::new("/var/lib/sgclaw"));
|
||||
@@ -66,7 +66,7 @@ fn zeroclaw_config_adapter_uses_deterministic_workspace_dir() {
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_skills_dir(Path::new("/var/lib/sgclaw"), &settings),
|
||||
zeroclaw_default_skills_dir(Path::new("/var/lib/sgclaw"))
|
||||
vec![zeroclaw_default_skills_dir(Path::new("/var/lib/sgclaw"))]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ fn deepseek_settings_reload_from_browser_config_path_after_file_changes() {
|
||||
assert_eq!(first.api_key, "sk-first");
|
||||
assert_eq!(first.base_url, "https://api.deepseek.com");
|
||||
assert_eq!(first.model, "deepseek-chat");
|
||||
assert_eq!(first.skills_dir, None);
|
||||
assert!(first.skills_dir.is_empty());
|
||||
|
||||
fs::write(
|
||||
&config_path,
|
||||
@@ -111,7 +111,7 @@ fn deepseek_settings_reload_from_browser_config_path_after_file_changes() {
|
||||
assert_eq!(second.api_key, "sk-second");
|
||||
assert_eq!(second.base_url, "https://proxy.example.com/v1");
|
||||
assert_eq!(second.model, "deepseek-reasoner");
|
||||
assert_eq!(second.skills_dir, Some(root.join("skill_lib")));
|
||||
assert_eq!(second.skills_dir, vec![root.join("skill_lib")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -122,12 +122,12 @@ fn resolve_skills_dir_prefers_nested_skills_subdirectory_for_configured_repo_roo
|
||||
api_key: "key".to_string(),
|
||||
base_url: "https://api.deepseek.com".to_string(),
|
||||
model: "deepseek-chat".to_string(),
|
||||
skills_dir: Some(root.join("skill_lib")),
|
||||
skills_dir: vec![root.join("skill_lib")],
|
||||
};
|
||||
|
||||
let resolved = resolve_skills_dir(&root, &settings);
|
||||
|
||||
assert_eq!(resolved, root.join("skill_lib/skills"));
|
||||
assert_eq!(resolved, vec![root.join("skill_lib/skills")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -139,12 +139,41 @@ fn resolve_skills_dir_preserves_absolute_configured_skills_directory() {
|
||||
api_key: "key".to_string(),
|
||||
base_url: "https://api.deepseek.com".to_string(),
|
||||
model: "deepseek-chat".to_string(),
|
||||
skills_dir: Some(external_skills.clone()),
|
||||
skills_dir: vec![external_skills.clone()],
|
||||
};
|
||||
|
||||
let resolved = resolve_skills_dir(&root, &settings);
|
||||
|
||||
assert_eq!(resolved, external_skills);
|
||||
assert_eq!(resolved, vec![external_skills]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_skills_dir_uses_skills_child_for_external_staged_root() {
|
||||
let root = std::env::temp_dir().join(format!("sgclaw-skills-{}", Uuid::new_v4()));
|
||||
let staged_root = root.join("external/skill_staging");
|
||||
fs::create_dir_all(staged_root.join("skills")).unwrap();
|
||||
fs::create_dir_all(staged_root.join("scenes")).unwrap();
|
||||
let settings = DeepSeekSettings {
|
||||
api_key: "key".to_string(),
|
||||
base_url: "https://api.deepseek.com".to_string(),
|
||||
model: "deepseek-chat".to_string(),
|
||||
skills_dir: vec![staged_root.clone()],
|
||||
};
|
||||
|
||||
let resolved = resolve_skills_dir(&root, &settings);
|
||||
|
||||
assert_eq!(resolved, vec![staged_root.join("skills")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_scene_skills_dir_path_prefers_staged_skills_child_under_project_root() {
|
||||
let root = std::env::temp_dir().join(format!("sgclaw-scene-skills-{}", Uuid::new_v4()));
|
||||
let top_level_skills = root.join("project/skills");
|
||||
fs::create_dir_all(top_level_skills.join("skill_staging/skills")).unwrap();
|
||||
|
||||
let resolved = resolve_scene_skills_dir_path(top_level_skills.clone());
|
||||
|
||||
assert_eq!(resolved, top_level_skills.join("skill_staging/skills"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -153,7 +182,7 @@ fn sgclaw_settings_default_to_compact_skills_and_browser_attached_profile() {
|
||||
"sk-test".to_string(),
|
||||
"https://api.deepseek.com".to_string(),
|
||||
"deepseek-chat".to_string(),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -187,7 +216,7 @@ fn sgclaw_settings_load_new_runtime_fields_from_browser_config() {
|
||||
|
||||
assert_eq!(settings.runtime_profile, RuntimeProfile::GeneralAssistant);
|
||||
assert_eq!(settings.skills_prompt_mode, SkillsPromptMode::Full);
|
||||
assert_eq!(settings.skills_dir, Some(root.join("skill_lib")));
|
||||
assert_eq!(settings.skills_dir, vec![root.join("skill_lib")]);
|
||||
assert_eq!(config.skills.prompt_injection_mode, SkillsPromptMode::Full);
|
||||
}
|
||||
|
||||
@@ -251,7 +280,7 @@ fn browser_attached_config_uses_low_temperature_for_deterministic_execution() {
|
||||
"sk-test".to_string(),
|
||||
"https://api.deepseek.com".to_string(),
|
||||
"deepseek-chat".to_string(),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user