diff --git a/crates/goose-cli/src/session/task_execution_display/mod.rs b/crates/goose-cli/src/session/task_execution_display/mod.rs index b0b208ed546e..434c777271a2 100644 --- a/crates/goose-cli/src/session/task_execution_display/mod.rs +++ b/crates/goose-cli/src/session/task_execution_display/mod.rs @@ -2,6 +2,7 @@ use goose::agents::subagent_execution_tool::lib::TaskStatus; use goose::agents::subagent_execution_tool::notification_events::{ TaskExecutionNotificationEvent, TaskInfo, }; +use goose::utils::safe_truncate; use serde_json::Value; use std::sync::atomic::{AtomicBool, Ordering}; @@ -18,7 +19,7 @@ static INITIAL_SHOWN: AtomicBool = AtomicBool::new(false); fn format_result_data_for_display(result_data: &Value) -> String { match result_data { - Value::String(s) => strip_ansi_codes(s), + Value::String(s) => s.to_string(), Value::Object(obj) => { if let Some(partial_output) = obj.get("partial_output").and_then(|v| v.as_str()) { format!("Partial output: {}", partial_output) @@ -45,53 +46,7 @@ fn process_output_for_display(output: &str) -> String { }; let clean_output = recent_lines.join(" ... "); - let stripped = strip_ansi_codes(&clean_output); - truncate_with_ellipsis(&stripped, OUTPUT_PREVIEW_LENGTH) -} - -fn truncate_with_ellipsis(text: &str, max_len: usize) -> String { - if text.len() > max_len { - let mut end = max_len.saturating_sub(3); - while end > 0 && !text.is_char_boundary(end) { - end -= 1; - } - format!("{}...", &text[..end]) - } else { - text.to_string() - } -} - -fn strip_ansi_codes(text: &str) -> String { - let mut result = String::new(); - let mut chars = text.chars(); - - while let Some(ch) = chars.next() { - if ch == '\x1b' { - if let Some(next_ch) = chars.next() { - if next_ch == '[' { - // This is an ANSI escape sequence, consume until alphabetic character - loop { - match chars.next() { - Some(c) if c.is_ascii_alphabetic() => break, - Some(_) => continue, - None => break, - } - } - } else { - // Not an ANSI sequence, keep both characters - result.push(ch); - result.push(next_ch); - } - } else { - // End of string after \x1b - result.push(ch); - } - } else { - result.push(ch); - } - } - - result + safe_truncate(&clean_output, OUTPUT_PREVIEW_LENGTH) } pub fn format_task_execution_notification( @@ -233,7 +188,7 @@ fn format_task_display(task: &TaskInfo) -> String { if matches!(task.status, TaskStatus::Failed) { if let Some(error) = &task.error { - let error_preview = truncate_with_ellipsis(error, 80); + let error_preview = safe_truncate(error, 80); task_display.push_str(&format!( " ⚠️ {}{}\n", error_preview.replace('\n', " "), diff --git a/crates/goose-cli/src/session/task_execution_display/tests.rs b/crates/goose-cli/src/session/task_execution_display/tests.rs index 725d161dff5b..7968af7da724 100644 --- a/crates/goose-cli/src/session/task_execution_display/tests.rs +++ b/crates/goose-cli/src/session/task_execution_display/tests.rs @@ -4,34 +4,6 @@ use goose::agents::subagent_execution_tool::notification_events::{ }; use serde_json::json; -#[test] -fn test_strip_ansi_codes() { - assert_eq!(strip_ansi_codes("hello world"), "hello world"); - assert_eq!(strip_ansi_codes("\x1b[31mred text\x1b[0m"), "red text"); - assert_eq!( - strip_ansi_codes("\x1b[1;32mbold green\x1b[0m"), - "bold green" - ); - assert_eq!( - strip_ansi_codes("normal\x1b[33myellow\x1b[0mnormal"), - "normalyellownormal" - ); - assert_eq!(strip_ansi_codes("\x1bhello"), "\x1bhello"); - assert_eq!(strip_ansi_codes("hello\x1b"), "hello\x1b"); - assert_eq!(strip_ansi_codes(""), ""); -} - -#[test] -fn test_truncate_with_ellipsis() { - assert_eq!(truncate_with_ellipsis("hello", 10), "hello"); - assert_eq!(truncate_with_ellipsis("hello", 5), "hello"); - assert_eq!(truncate_with_ellipsis("hello world", 8), "hello..."); - assert_eq!(truncate_with_ellipsis("hello", 3), "..."); - assert_eq!(truncate_with_ellipsis("hello", 2), "..."); - assert_eq!(truncate_with_ellipsis("hello", 1), "..."); - assert_eq!(truncate_with_ellipsis("", 5), ""); -} - #[test] fn test_process_output_for_display() { assert_eq!(process_output_for_display("hello world"), "hello world"); @@ -49,20 +21,15 @@ fn test_process_output_for_display() { assert!(result.len() <= 100); assert!(result.ends_with("...")); - let ansi_output = "\x1b[31mred line 1\x1b[0m\n\x1b[32mgreen line 2\x1b[0m"; - let result = process_output_for_display(ansi_output); - assert_eq!(result, "red line 1 ... green line 2"); - assert_eq!(process_output_for_display(""), ""); } #[test] fn test_format_result_data_for_display() { - let string_val = json!("hello world"); - assert_eq!(format_result_data_for_display(&string_val), "hello world"); - - let ansi_string = json!("\x1b[31mred text\x1b[0m"); - assert_eq!(format_result_data_for_display(&ansi_string), "red text"); + assert_eq!( + format_result_data_for_display(&json!("red text")), + "red text" + ); assert_eq!(format_result_data_for_display(&json!(true)), "true"); assert_eq!(format_result_data_for_display(&json!(false)), "false"); diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index befc27f0d920..7d6819f8dfda 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -305,6 +305,7 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema); super::routes::config_management::providers, super::routes::config_management::upsert_permissions, super::routes::agent::get_tools, + super::routes::agent::add_sub_recipes, super::routes::reply::confirm_permission, super::routes::context::manage_context, super::routes::session::list_sessions, @@ -394,6 +395,8 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema); goose::recipe::SubRecipe, goose::agents::types::RetryConfig, goose::agents::types::SuccessCheck, + super::routes::agent::AddSubRecipesRequest, + super::routes::agent::AddSubRecipesResponse, )) )] pub struct ApiDoc; diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 7167dcae5737..a896cdb2e6d7 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -6,7 +6,6 @@ use axum::{ routing::{get, post}, Json, Router, }; -use goose::config::Config; use goose::config::PermissionManager; use goose::model::ModelConfig; use goose::providers::create; @@ -15,6 +14,7 @@ use goose::{ agents::{extension::ToolInfo, extension_manager::get_parameter_names}, config::permission::PermissionLevel, }; +use goose::{config::Config, recipe::SubRecipe}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; @@ -35,6 +35,16 @@ struct ExtendPromptResponse { success: bool, } +#[derive(Deserialize, utoipa::ToSchema)] +pub struct AddSubRecipesRequest { + sub_recipes: Vec, +} + +#[derive(Serialize, utoipa::ToSchema)] +pub struct AddSubRecipesResponse { + success: bool, +} + #[derive(Deserialize)] struct ProviderFile { name: String, @@ -88,6 +98,30 @@ async fn get_versions() -> Json { }) } +#[utoipa::path( + post, + path = "/agent/add_sub_recipes", + request_body = AddSubRecipesRequest, + responses( + (status = 200, description = "added sub recipes to agent successfully", body = AddSubRecipesResponse), + (status = 401, description = "Unauthorized - invalid secret key"), + ), +)] +async fn add_sub_recipes( + State(state): State>, + headers: HeaderMap, + Json(payload): Json, +) -> Result, StatusCode> { + verify_secret_key(&headers, &state)?; + + let agent = state + .get_agent() + .await + .map_err(|_| StatusCode::PRECONDITION_FAILED)?; + agent.add_sub_recipes(payload.sub_recipes.clone()).await; + Ok(Json(AddSubRecipesResponse { success: true })) +} + async fn extend_prompt( State(state): State>, headers: HeaderMap, @@ -318,5 +352,6 @@ pub fn routes(state: Arc) -> Router { post(update_router_tool_selector), ) .route("/agent/session_config", post(update_session_config)) + .route("/agent/add_sub_recipes", post(add_sub_recipes)) .with_state(state) } diff --git a/crates/goose/src/agents/subagent_execution_tool/executor/mod.rs b/crates/goose/src/agents/subagent_execution_tool/executor/mod.rs index 665bf9e14b64..7aaa14c79f64 100644 --- a/crates/goose/src/agents/subagent_execution_tool/executor/mod.rs +++ b/crates/goose/src/agents/subagent_execution_tool/executor/mod.rs @@ -193,15 +193,7 @@ async fn collect_results( expected_count: usize, ) -> Vec { let mut results = Vec::new(); - while let Some(mut result) = result_rx.recv().await { - // Truncate data to 650 chars if needed - if let Some(data) = result.data.as_mut() { - if let Some(data_str) = data.as_str() { - if data_str.len() > 650 { - *data = serde_json::Value::String(format!("{}...", &data_str[..650])); - } - } - } + while let Some(result) = result_rx.recv().await { task_execution_tracker .complete_task(&result.task_id, result.clone()) .await; diff --git a/crates/goose/src/agents/subagent_execution_tool/tasks.rs b/crates/goose/src/agents/subagent_execution_tool/tasks.rs index 4ecd5b628ffa..64ee4ff2461d 100644 --- a/crates/goose/src/agents/subagent_execution_tool/tasks.rs +++ b/crates/goose/src/agents/subagent_execution_tool/tasks.rs @@ -8,6 +8,7 @@ use tokio_util::sync::CancellationToken; use crate::agents::subagent_execution_tool::task_execution_tracker::TaskExecutionTracker; use crate::agents::subagent_execution_tool::task_types::{Task, TaskResult, TaskStatus}; +use crate::agents::subagent_execution_tool::utils::strip_ansi_codes; use crate::agents::subagent_handler::run_complete_subagent_task; use crate::agents::subagent_task_config::TaskConfig; @@ -70,7 +71,7 @@ async fn get_task_result( if success { process_output(stdout_output) } else { - Err(format!("Command failed:\n{}", stderr_output)) + Err(format!("Command failed:\n{}", &stderr_output)) } } } @@ -224,6 +225,7 @@ fn spawn_output_reader( let mut buffer = String::new(); let mut lines = BufReader::new(reader).lines(); while let Ok(Some(line)) = lines.next_line().await { + let line = strip_ansi_codes(&line); buffer.push_str(&line); buffer.push('\n'); diff --git a/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs b/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs index 5d75675283d3..e1e48835c7c3 100644 --- a/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs +++ b/crates/goose/src/agents/subagent_execution_tool/utils/mod.rs @@ -23,5 +23,38 @@ pub fn count_by_status(tasks: &HashMap) -> (usize, usize, usiz (total, pending, running, completed, failed) } +pub fn strip_ansi_codes(text: &str) -> String { + let mut result = String::new(); + let mut chars = text.chars(); + + while let Some(ch) = chars.next() { + if ch == '\x1b' { + if let Some(next_ch) = chars.next() { + if next_ch == '[' { + // This is an ANSI escape sequence, consume until alphabetic character + loop { + match chars.next() { + Some(c) if c.is_ascii_alphabetic() => break, + Some(_) => continue, + None => break, + } + } + } else { + // Not an ANSI sequence, keep both characters + result.push(ch); + result.push(next_ch); + } + } else { + // End of string after \x1b + result.push(ch); + } + } else { + result.push(ch); + } + } + + result +} + #[cfg(test)] mod tests; diff --git a/crates/goose/src/agents/subagent_execution_tool/utils/tests.rs b/crates/goose/src/agents/subagent_execution_tool/utils/tests.rs index b4e7f757b420..253d28ebff00 100644 --- a/crates/goose/src/agents/subagent_execution_tool/utils/tests.rs +++ b/crates/goose/src/agents/subagent_execution_tool/utils/tests.rs @@ -1,5 +1,7 @@ use crate::agents::subagent_execution_tool::task_types::{Task, TaskInfo, TaskStatus}; -use crate::agents::subagent_execution_tool::utils::{count_by_status, get_task_name}; +use crate::agents::subagent_execution_tool::utils::{ + count_by_status, get_task_name, strip_ansi_codes, +}; use serde_json::json; use std::collections::HashMap; @@ -152,3 +154,24 @@ mod count_by_status { ); } } + +mod strip_ansi_codes { + use super::*; + + #[test] + fn test_strip_ansi_codes() { + assert_eq!(strip_ansi_codes("hello world"), "hello world"); + assert_eq!(strip_ansi_codes("\x1b[31mred text\x1b[0m"), "red text"); + assert_eq!( + strip_ansi_codes("\x1b[1;32mbold green\x1b[0m"), + "bold green" + ); + assert_eq!( + strip_ansi_codes("normal\x1b[33myellow\x1b[0mnormal"), + "normalyellownormal" + ); + assert_eq!(strip_ansi_codes("\x1bhello"), "\x1bhello"); + assert_eq!(strip_ansi_codes("hello\x1b"), "hello\x1b"); + assert_eq!(strip_ansi_codes(""), ""); + } +} diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 3fa2f9e0b322..a567e05ea012 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -13,6 +13,39 @@ "version": "1.1.0" }, "paths": { + "/agent/add_sub_recipes": { + "post": { + "tags": [ + "super::routes::agent" + ], + "operationId": "add_sub_recipes", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddSubRecipesRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "added sub recipes to agent successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddSubRecipesResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - invalid secret key" + } + } + } + }, "/agent/tools": { "get": { "tags": [ @@ -1033,6 +1066,31 @@ }, "components": { "schemas": { + "AddSubRecipesRequest": { + "type": "object", + "required": [ + "sub_recipes" + ], + "properties": { + "sub_recipes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SubRecipe" + } + } + } + }, + "AddSubRecipesResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean" + } + } + }, "Annotations": { "type": "object", "properties": { diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index c772a1b0048b..d3b39f08533a 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen'; +import type { AddSubRecipesData, AddSubRecipesResponse2, GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen'; import { client as _heyApiClient } from './client.gen'; export type Options = ClientOptions & { @@ -18,6 +18,17 @@ export type Options; }; +export const addSubRecipes = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/agent/add_sub_recipes', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + export const getTools = (options?: Options) => { return (options?.client ?? _heyApiClient).get({ url: '/agent/tools', diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index 655688b7ef71..924b74a5717b 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -1,5 +1,13 @@ // This file is auto-generated by @hey-api/openapi-ts +export type AddSubRecipesRequest = { + sub_recipes: Array; +}; + +export type AddSubRecipesResponse = { + success: boolean; +}; + export type Annotations = { audience?: Array; priority?: number; @@ -777,6 +785,29 @@ export type UpsertPermissionsQuery = { tool_permissions: Array; }; +export type AddSubRecipesData = { + body: AddSubRecipesRequest; + path?: never; + query?: never; + url: '/agent/add_sub_recipes'; +}; + +export type AddSubRecipesErrors = { + /** + * Unauthorized - invalid secret key + */ + 401: unknown; +}; + +export type AddSubRecipesResponses = { + /** + * added sub recipes to agent successfully + */ + 200: AddSubRecipesResponse; +}; + +export type AddSubRecipesResponse2 = AddSubRecipesResponses[keyof AddSubRecipesResponses]; + export type GetToolsData = { body?: never; path?: never; diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 84ac0b526876..e1f9a7cba28a 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -197,7 +197,6 @@ if (process.platform === 'win32') { const protocolUrl = commandLine.find((arg) => arg.startsWith('goose://')); if (protocolUrl) { const parsedUrl = new URL(protocolUrl); - // If it's a bot/recipe URL, handle it directly by creating a new window if (parsedUrl.hostname === 'bot' || parsedUrl.hostname === 'recipe') { app.whenReady().then(async () => { diff --git a/ui/desktop/src/recipe/add_sub_recipe_on_agent.ts b/ui/desktop/src/recipe/add_sub_recipe_on_agent.ts new file mode 100644 index 000000000000..448e4d5f0068 --- /dev/null +++ b/ui/desktop/src/recipe/add_sub_recipe_on_agent.ts @@ -0,0 +1,10 @@ +import { addSubRecipes, SubRecipe } from '../api'; + +export async function addSubRecipesToAgent(subRecipes: SubRecipe[]) { + const add_sub_recipe_response = await addSubRecipes({ body: { sub_recipes: subRecipes } }); + if (add_sub_recipe_response.error) { + console.warn(`Failed to add sub recipes: ${add_sub_recipe_response.error}`); + } else { + console.log('Added sub recipes'); + } +} diff --git a/ui/desktop/src/utils/providerUtils.ts b/ui/desktop/src/utils/providerUtils.ts index 44f3636acc4d..a6c089a0118d 100644 --- a/ui/desktop/src/utils/providerUtils.ts +++ b/ui/desktop/src/utils/providerUtils.ts @@ -10,7 +10,13 @@ import { extractExtensionConfig } from '../components/settings/extensions/utils' import type { ExtensionConfig, FixedExtensionEntry } from '../components/ConfigContext'; // TODO: remove when removing migration logic import { toastService } from '../toasts'; -import { ExtensionQuery, addExtension as apiAddExtension } from '../api'; +import { + ExtensionQuery, + RecipeParameter, + SubRecipe, + addExtension as apiAddExtension, +} from '../api'; +import { addSubRecipesToAgent } from '../recipe/add_sub_recipe_on_agent'; export interface Provider { id: string; // Lowercase key (e.g., "openai") @@ -71,15 +77,19 @@ const substituteParameters = (text: string, params: Record): str */ export const updateSystemPromptWithParameters = async ( recipeParameters: Record, - recipeConfig?: { instructions?: string | null } + recipeConfig?: { + instructions?: string | null; + sub_recipes?: SubRecipe[] | null; + parameters?: RecipeParameter[] | null; + } ): Promise => { + const subRecipes = recipeConfig?.sub_recipes; try { const originalInstructions = recipeConfig?.instructions; if (!originalInstructions) { return; } - // Substitute parameters in the instructions const substitutedInstructions = substituteParameters(originalInstructions, recipeParameters); @@ -103,6 +113,16 @@ export const updateSystemPromptWithParameters = async ( } catch (error) { console.error('Error updating system prompt with parameters:', error); } + if (subRecipes && subRecipes?.length > 0) { + for (const subRecipe of subRecipes) { + if (subRecipe.values) { + for (const key in subRecipe.values) { + subRecipe.values[key] = substituteParameters(subRecipe.values[key], recipeParameters); + } + } + } + await addSubRecipesToAgent(subRecipes); + } }; /** @@ -192,9 +212,16 @@ export const initializeSystem = async ( // Get recipeConfig directly here const recipeConfig = window.appConfig?.get?.('recipe'); - const botPrompt = (recipeConfig as { instructions?: string })?.instructions; + const recipe_instructions = (recipeConfig as { instructions?: string })?.instructions; const responseConfig = (recipeConfig as { response?: { json_schema?: unknown } })?.response; - + const subRecipes = (recipeConfig as { sub_recipes?: SubRecipe[] })?.sub_recipes; + const parameters = (recipeConfig as { parameters?: RecipeParameter[] })?.parameters; + const hasParameters = parameters && parameters?.length > 0; + const hasSubRecipes = subRecipes && subRecipes?.length > 0; + let prompt = desktopPrompt; + if (!hasParameters && recipe_instructions) { + prompt = `${desktopPromptBot}\nIMPORTANT instructions for you to operate as agent:\n${recipe_instructions}`; + } // Extend the system prompt with desktop-specific information const response = await fetch(getApiUrl('/agent/prompt'), { method: 'POST', @@ -203,21 +230,17 @@ export const initializeSystem = async ( 'X-Secret-Key': getSecretKey(), }, body: JSON.stringify({ - extension: botPrompt - ? `${desktopPromptBot}\nIMPORTANT instructions for you to operate as agent:\n${botPrompt}` - : desktopPrompt, + extension: prompt, }), }); - if (!response.ok) { console.warn(`Failed to extend system prompt: ${response.statusText}`); } else { console.log('Extended system prompt with desktop-specific information'); - if (botPrompt) { - console.log('Added custom bot prompt to system prompt'); - } } - + if (!hasParameters && hasSubRecipes) { + await addSubRecipesToAgent(subRecipes); + } // Configure session with response config if present if (responseConfig?.json_schema) { const sessionConfigResponse = await fetch(getApiUrl('/agent/session_config'), {