Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/goose-server/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ pub async fn run() -> Result<()> {
.join("schedules.json");

let scheduler_instance = SchedulerFactory::create(schedule_file_path).await?;
app_state.set_scheduler(scheduler_instance).await;
app_state.set_scheduler(scheduler_instance.clone()).await;

// NEW: Provide scheduler access to the agent
agent_ref.set_scheduler(scheduler_instance).await;

let cors = CorsLayer::new()
.allow_origin(Any)
Expand Down
10 changes: 9 additions & 1 deletion crates/goose-server/src/routes/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ fn parse_session_name_to_iso(session_name: &str) -> String {
request_body = CreateScheduleRequest,
responses(
(status = 200, description = "Scheduled job created successfully", body = ScheduledJob),
(status = 400, description = "Invalid cron expression or recipe file"),
(status = 409, description = "Job ID already exists"),
(status = 500, description = "Internal server error")
),
tag = "schedule"
Expand Down Expand Up @@ -128,7 +130,13 @@ async fn create_schedule(
.await
.map_err(|e| {
eprintln!("Error creating schedule: {:?}", e); // Log error
StatusCode::INTERNAL_SERVER_ERROR
match e {
goose::scheduler::SchedulerError::JobNotFound(_) => StatusCode::NOT_FOUND,
goose::scheduler::SchedulerError::CronParseError(_) => StatusCode::BAD_REQUEST,
goose::scheduler::SchedulerError::RecipeLoadError(_) => StatusCode::BAD_REQUEST,
goose::scheduler::SchedulerError::JobIdExists(_) => StatusCode::CONFLICT,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
})?;
Ok(Json(job))
}
Expand Down
24 changes: 21 additions & 3 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::permission::PermissionConfirmation;
use crate::providers::base::Provider;
use crate::providers::errors::ProviderError;
use crate::recipe::{Author, Recipe, Settings};
use crate::scheduler_trait::SchedulerTrait;
use crate::tool_monitor::{ToolCall, ToolMonitor};
use regex::Regex;
use serde_json::Value;
Expand All @@ -27,7 +28,8 @@ use crate::agents::extension::{ExtensionConfig, ExtensionError, ExtensionResult,
use crate::agents::extension_manager::{get_parameter_names, ExtensionManager};
use crate::agents::platform_tools::{
PLATFORM_LIST_RESOURCES_TOOL_NAME, PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME,
PLATFORM_READ_RESOURCE_TOOL_NAME, PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME,
PLATFORM_MANAGE_SCHEDULE_TOOL_NAME, PLATFORM_READ_RESOURCE_TOOL_NAME,
PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME,
};
use crate::agents::prompt_manager::PromptManager;
use crate::agents::router_tool_selector::{
Expand Down Expand Up @@ -59,6 +61,7 @@ pub struct Agent {
pub(super) tool_result_rx: ToolResultReceiver,
pub(super) tool_monitor: Mutex<Option<ToolMonitor>>,
pub(super) router_tool_selector: Mutex<Option<Arc<Box<dyn RouterToolSelector>>>>,
pub(super) scheduler_service: Mutex<Option<Arc<dyn SchedulerTrait>>>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -86,6 +89,7 @@ impl Agent {
tool_result_rx: Arc::new(Mutex::new(tool_rx)),
tool_monitor: Mutex::new(None),
router_tool_selector: Mutex::new(None),
scheduler_service: Mutex::new(None),
}
}

Expand All @@ -104,6 +108,12 @@ impl Agent {
monitor.reset();
}
}

/// Set the scheduler service for this agent
pub async fn set_scheduler(&self, scheduler: Arc<dyn SchedulerTrait>) {
let mut scheduler_service = self.scheduler_service.lock().await;
*scheduler_service = Some(scheduler);
}
}

impl Default for Agent {
Expand Down Expand Up @@ -185,7 +195,7 @@ impl Agent {

/// Dispatch a single tool call to the appropriate client
#[instrument(skip(self, tool_call, request_id), fields(input, output))]
pub(super) async fn dispatch_tool_call(
pub async fn dispatch_tool_call(
&self,
tool_call: mcp_core::tool::ToolCall,
request_id: String,
Expand All @@ -204,6 +214,13 @@ impl Agent {
}
}

if tool_call.name == PLATFORM_MANAGE_SCHEDULE_TOOL_NAME {
let result = self
.handle_schedule_management(tool_call.arguments, request_id.clone())
.await;
return (request_id, Ok(ToolCallResult::from(result)));
}

if tool_call.name == PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME {
let extension_name = tool_call
.arguments
Expand Down Expand Up @@ -414,7 +431,7 @@ impl Agent {
let mut extension_manager = self.extension_manager.lock().await;
extension_manager.add_extension(extension.clone()).await?;
}
};
}

// If vector tool selection is enabled, index the tools
let selector = self.router_tool_selector.lock().await.clone();
Expand Down Expand Up @@ -453,6 +470,7 @@ impl Agent {
// Add platform tools
prefixed_tools.push(platform_tools::search_available_extensions_tool());
prefixed_tools.push(platform_tools::manage_extensions_tool());
prefixed_tools.push(platform_tools::manage_schedule_tool());

// Add resource tools if supported
if extension_manager.supports_resources() {
Expand Down
2 changes: 2 additions & 0 deletions crates/goose/src/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub mod prompt_manager;
mod reply_parts;
mod router_tool_selector;
mod router_tools;
mod schedule_tool;

mod tool_execution;
mod tool_router_index_manager;
pub(crate) mod tool_vectordb;
Expand Down
45 changes: 45 additions & 0 deletions crates/goose/src/agents/platform_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub const PLATFORM_LIST_RESOURCES_TOOL_NAME: &str = "platform__list_resources";
pub const PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME: &str =
"platform__search_available_extensions";
pub const PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME: &str = "platform__manage_extensions";
pub const PLATFORM_MANAGE_SCHEDULE_TOOL_NAME: &str = "platform__manage_schedule";

pub fn read_resource_tool() -> Tool {
Tool::new(
Expand Down Expand Up @@ -112,3 +113,47 @@ pub fn manage_extensions_tool() -> Tool {
}),
)
}

pub fn manage_schedule_tool() -> Tool {
Tool::new(
PLATFORM_MANAGE_SCHEDULE_TOOL_NAME.to_string(),
indoc! {r#"
Manage scheduled recipe execution for this Goose instance.

Actions:
- "list": List all scheduled jobs
- "create": Create a new scheduled job from a recipe file
- "run_now": Execute a scheduled job immediately
- "pause": Pause a scheduled job
- "unpause": Resume a paused job
- "delete": Remove a scheduled job
- "kill": Terminate a currently running job
- "inspect": Get details about a running job
- "sessions": List execution history for a job
- "session_content": Get the full content (messages) of a specific session
"#}
.to_string(),
json!({
"type": "object",
"required": ["action"],
"properties": {
"action": {
"type": "string",
"enum": ["list", "create", "run_now", "pause", "unpause", "delete", "kill", "inspect", "sessions", "session_content"]
},
"job_id": {"type": "string", "description": "Job identifier for operations on existing jobs"},
"recipe_path": {"type": "string", "description": "Path to recipe file for create action"},
"cron_expression": {"type": "string", "description": "A six field cron expression for create action"},
"limit": {"type": "integer", "description": "Limit for sessions list", "default": 50},
"session_id": {"type": "string", "description": "Session identifier for session_content action"}
}
}),
Some(ToolAnnotations {
title: Some("Manage scheduled recipes".to_string()),
read_only_hint: false,
destructive_hint: true, // Can kill jobs
idempotent_hint: false,
open_world_hint: false,
}),
)
}
Loading
Loading