feat(summon): add context parameter for delegate tasks (#9518)
Signed-off-by: Douwe M Osinga <douwe@sidewalklabs.com> Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
@@ -58,6 +58,7 @@ pub struct DelegateParams {
|
||||
pub model: Option<String>,
|
||||
pub temperature: Option<f32>,
|
||||
pub max_turns: Option<usize>,
|
||||
pub context: Option<String>,
|
||||
pub working_dir: Option<String>,
|
||||
#[serde(default)]
|
||||
pub r#async: bool,
|
||||
@@ -296,6 +297,14 @@ pub fn discover_filesystem_sources(working_dir: &Path) -> Vec<SourceEntry> {
|
||||
sources
|
||||
}
|
||||
|
||||
fn build_instructions_with_context(context: &str, instructions: &str) -> String {
|
||||
let mut result = format!("# Reference Context\n\n{}", context);
|
||||
if !instructions.is_empty() {
|
||||
result.push_str(&format!("\n\n# Task Instructions\n\n{}", instructions));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn build_subagent_instructions(session: Option<&crate::session::Session>) -> String {
|
||||
let Some(session) = session else {
|
||||
return String::new();
|
||||
@@ -552,6 +561,10 @@ impl SummonClient {
|
||||
"minimum": 1,
|
||||
"description": "Maximum turns for this delegate. Overrides recipe settings.max_turns and GOOSE_SUBAGENT_MAX_TURNS."
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "Reference context to inject into the delegate's system prompt. Use for background information, file contents, or constraints the delegate needs but that aren't part of the task instructions."
|
||||
},
|
||||
"working_dir": {
|
||||
"type": "string",
|
||||
"description": "Working directory for the delegate. Must be within the parent session's working directory. Defaults to the parent's working directory."
|
||||
@@ -1232,12 +1245,19 @@ impl SummonClient {
|
||||
session_id: &str,
|
||||
working_dir: &Path,
|
||||
) -> Result<Recipe, String> {
|
||||
if let Some(source_name) = ¶ms.source {
|
||||
let mut recipe = if let Some(source_name) = ¶ms.source {
|
||||
self.build_source_recipe(source_name, params, session_id, working_dir)
|
||||
.await
|
||||
.await?
|
||||
} else {
|
||||
self.build_adhoc_recipe(params)
|
||||
self.build_adhoc_recipe(params)?
|
||||
};
|
||||
|
||||
if let Some(ref context) = params.context {
|
||||
let existing = recipe.instructions.unwrap_or_default();
|
||||
recipe.instructions = Some(build_instructions_with_context(context, &existing));
|
||||
}
|
||||
|
||||
Ok(recipe)
|
||||
}
|
||||
|
||||
fn build_adhoc_recipe(&self, params: &DelegateParams) -> Result<Recipe, String> {
|
||||
@@ -2255,6 +2275,41 @@ You review code."#;
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_context_injected_into_adhoc_recipe() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let client = SummonClient::new(create_test_context()).unwrap();
|
||||
|
||||
let params = DelegateParams {
|
||||
instructions: Some("do the task".to_string()),
|
||||
context: Some("background info".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let recipe = client
|
||||
.build_delegate_recipe(¶ms, "test", temp_dir.path())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
recipe.instructions.as_deref(),
|
||||
Some("# Reference Context\n\nbackground info")
|
||||
);
|
||||
assert_eq!(recipe.prompt.as_deref(), Some("do the task"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_instructions_with_context_wraps_existing_instructions() {
|
||||
assert_eq!(
|
||||
build_instructions_with_context("background info", "Run deploy steps"),
|
||||
"# Reference Context\n\nbackground info\n\n# Task Instructions\n\nRun deploy steps"
|
||||
);
|
||||
assert_eq!(
|
||||
build_instructions_with_context("background info", ""),
|
||||
"# Reference Context\n\nbackground info"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_delegate_params_rejects_zero_max_turns() {
|
||||
let context = create_test_context();
|
||||
|
||||
Reference in New Issue
Block a user