Platform Extension MOIM (Minus One Info Message) (#5027)
This commit is contained in:
Generated
+1
@@ -2755,6 +2755,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"serial_test",
|
||||||
"shlex",
|
"shlex",
|
||||||
"tar",
|
"tar",
|
||||||
"temp-env",
|
"temp-env",
|
||||||
|
|||||||
@@ -69,3 +69,4 @@ tempfile = "3"
|
|||||||
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
||||||
test-case = "3.3"
|
test-case = "3.3"
|
||||||
tokio = { version = "1.43", features = ["rt", "macros"] }
|
tokio = { version = "1.43", features = ["rt", "macros"] }
|
||||||
|
serial_test = "3.2.0"
|
||||||
|
|||||||
@@ -871,7 +871,7 @@ print(\"hello, world\")
|
|||||||
format_tool_name("platform__manage_extensions"),
|
format_tool_name("platform__manage_extensions"),
|
||||||
"Platform: Manage Extensions"
|
"Platform: Manage Extensions"
|
||||||
);
|
);
|
||||||
assert_eq!(format_tool_name("todo__read"), "Todo: Read");
|
assert_eq!(format_tool_name("todo__write"), "Todo: Write");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ where
|
|||||||
use goose::config::ExtensionConfig;
|
use goose::config::ExtensionConfig;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
goose::agents::moim::SKIP.with(|f| f.set(true));
|
||||||
|
|
||||||
if let Ok(path) = dotenv() {
|
if let Ok(path) = dotenv() {
|
||||||
println!("Loaded environment from {:?}", path);
|
println!("Loaded environment from {:?}", path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) {
|
|||||||
"developer__text_editor" => render_text_editor_request(call, debug),
|
"developer__text_editor" => render_text_editor_request(call, debug),
|
||||||
"developer__shell" => render_shell_request(call, debug),
|
"developer__shell" => render_shell_request(call, debug),
|
||||||
"dynamic_task__create_task" => render_dynamic_task_request(call, debug),
|
"dynamic_task__create_task" => render_dynamic_task_request(call, debug),
|
||||||
"todo__read" | "todo__write" => render_todo_request(call, debug),
|
"todo__write" => render_todo_request(call, debug),
|
||||||
_ => render_default_request(call, debug),
|
_ => render_default_request(call, debug),
|
||||||
},
|
},
|
||||||
Err(e) => print_markdown(&e.to_string(), theme),
|
Err(e) => print_markdown(&e.to_string(), theme),
|
||||||
@@ -505,13 +505,9 @@ fn render_dynamic_task_request(call: &CallToolRequestParam, debug: bool) {
|
|||||||
fn render_todo_request(call: &CallToolRequestParam, _debug: bool) {
|
fn render_todo_request(call: &CallToolRequestParam, _debug: bool) {
|
||||||
print_tool_header(call);
|
print_tool_header(call);
|
||||||
|
|
||||||
// For todo tools, always show the full content without redaction
|
|
||||||
if let Some(args) = &call.arguments {
|
if let Some(args) = &call.arguments {
|
||||||
if let Some(Value::String(content)) = args.get("content") {
|
if let Some(Value::String(content)) = args.get("content") {
|
||||||
println!("{}: {}", style("content").dim(), style(content).green());
|
println!("{}: {}", style("content").dim(), style(content).green());
|
||||||
} else {
|
|
||||||
// For todo__read, there are no arguments
|
|
||||||
// Just print an empty line for consistency
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
|
|||||||
@@ -946,10 +946,15 @@ impl Agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let conversation_with_moim = super::moim::inject_moim(
|
||||||
|
conversation.clone(),
|
||||||
|
&self.extension_manager,
|
||||||
|
).await;
|
||||||
|
|
||||||
let mut stream = Self::stream_response_from_provider(
|
let mut stream = Self::stream_response_from_provider(
|
||||||
self.provider().await?,
|
self.provider().await?,
|
||||||
&system_prompt,
|
&system_prompt,
|
||||||
conversation.messages(),
|
conversation_with_moim.messages(),
|
||||||
&tools,
|
&tools,
|
||||||
&toolshim_tools,
|
&toolshim_tools,
|
||||||
).await?;
|
).await?;
|
||||||
|
|||||||
@@ -1163,6 +1163,28 @@ impl ExtensionManager {
|
|||||||
.get(&name.into())
|
.get(&name.into())
|
||||||
.map(|ext| ext.get_client())
|
.map(|ext| ext.get_client())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn collect_moim(&self) -> Option<String> {
|
||||||
|
let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
|
||||||
|
let mut content = format!("<info-msg>\nDatetime: {}\n", timestamp);
|
||||||
|
|
||||||
|
let extensions = self.extensions.lock().await;
|
||||||
|
for (name, extension) in extensions.iter() {
|
||||||
|
if let ExtensionConfig::Platform { .. } = &extension.config {
|
||||||
|
let client = extension.get_client();
|
||||||
|
let client_guard = client.lock().await;
|
||||||
|
if let Some(moim_content) = client_guard.get_moim().await {
|
||||||
|
tracing::debug!("MOIM content from {}: {} chars", name, moim_content.len());
|
||||||
|
content.push('\n');
|
||||||
|
content.push_str(&moim_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content.push_str("\n</info-msg>");
|
||||||
|
|
||||||
|
Some(content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ pub trait McpClientTrait: Send + Sync {
|
|||||||
async fn subscribe(&self) -> mpsc::Receiver<ServerNotification>;
|
async fn subscribe(&self) -> mpsc::Receiver<ServerNotification>;
|
||||||
|
|
||||||
fn get_info(&self) -> Option<&InitializeResult>;
|
fn get_info(&self) -> Option<&InitializeResult>;
|
||||||
|
|
||||||
|
async fn get_moim(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GooseClient {
|
pub struct GooseClient {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub mod final_output_tool;
|
|||||||
mod large_response_handler;
|
mod large_response_handler;
|
||||||
pub mod mcp_client;
|
pub mod mcp_client;
|
||||||
pub mod model_selector;
|
pub mod model_selector;
|
||||||
|
pub mod moim;
|
||||||
pub mod platform_tools;
|
pub mod platform_tools;
|
||||||
pub mod prompt_manager;
|
pub mod prompt_manager;
|
||||||
pub mod recipe_tools;
|
pub mod recipe_tools;
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
use crate::agents::extension_manager::ExtensionManager;
|
||||||
|
use crate::conversation::message::Message;
|
||||||
|
use crate::conversation::{fix_conversation, Conversation};
|
||||||
|
use rmcp::model::Role;
|
||||||
|
|
||||||
|
// Test-only utility. Do not use in production code. No `test` directive due to call outside crate.
|
||||||
|
thread_local! {
|
||||||
|
pub static SKIP: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn inject_moim(
|
||||||
|
conversation: Conversation,
|
||||||
|
extension_manager: &ExtensionManager,
|
||||||
|
) -> Conversation {
|
||||||
|
if SKIP.with(|f| f.get()) {
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(moim) = extension_manager.collect_moim().await {
|
||||||
|
let mut messages = conversation.messages().clone();
|
||||||
|
let idx = messages
|
||||||
|
.iter()
|
||||||
|
.rposition(|m| m.role == Role::Assistant)
|
||||||
|
.unwrap_or(0);
|
||||||
|
messages.insert(idx, Message::user().with_text(moim));
|
||||||
|
|
||||||
|
let (fixed, issues) = fix_conversation(Conversation::new_unvalidated(messages));
|
||||||
|
|
||||||
|
let has_unexpected_issues = issues
|
||||||
|
.iter()
|
||||||
|
.any(|issue| !issue.contains("Merged consecutive user messages"));
|
||||||
|
|
||||||
|
if has_unexpected_issues {
|
||||||
|
tracing::warn!("MOIM injection caused unexpected issues: {:?}", issues);
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
conversation
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rmcp::model::CallToolRequestParam;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_moim_injection_before_assistant() {
|
||||||
|
let em = ExtensionManager::new_without_provider();
|
||||||
|
|
||||||
|
let conv = Conversation::new_unvalidated(vec![
|
||||||
|
Message::user().with_text("Hello"),
|
||||||
|
Message::assistant().with_text("Hi"),
|
||||||
|
Message::user().with_text("Bye"),
|
||||||
|
]);
|
||||||
|
let result = inject_moim(conv, &em).await;
|
||||||
|
let msgs = result.messages();
|
||||||
|
|
||||||
|
assert_eq!(msgs.len(), 3);
|
||||||
|
assert_eq!(msgs[0].content[0].as_text().unwrap(), "Hello");
|
||||||
|
assert_eq!(msgs[1].content[0].as_text().unwrap(), "Hi");
|
||||||
|
|
||||||
|
let merged_content = msgs[0]
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.filter_map(|c| c.as_text())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("");
|
||||||
|
assert!(merged_content.contains("Hello"));
|
||||||
|
assert!(merged_content.contains("<info-msg>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_moim_injection_no_assistant() {
|
||||||
|
let em = ExtensionManager::new_without_provider();
|
||||||
|
|
||||||
|
let conv = Conversation::new_unvalidated(vec![Message::user().with_text("Hello")]);
|
||||||
|
let result = inject_moim(conv, &em).await;
|
||||||
|
|
||||||
|
assert_eq!(result.messages().len(), 1);
|
||||||
|
|
||||||
|
let merged_content = result.messages()[0]
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.filter_map(|c| c.as_text())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("");
|
||||||
|
assert!(merged_content.contains("Hello"));
|
||||||
|
assert!(merged_content.contains("<info-msg>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_moim_with_tool_calls() {
|
||||||
|
let em = ExtensionManager::new_without_provider();
|
||||||
|
|
||||||
|
let conv = Conversation::new_unvalidated(vec![
|
||||||
|
Message::user().with_text("Search for something"),
|
||||||
|
Message::assistant()
|
||||||
|
.with_text("I'll search for you")
|
||||||
|
.with_tool_request(
|
||||||
|
"search_1",
|
||||||
|
Ok(CallToolRequestParam {
|
||||||
|
name: "search".into(),
|
||||||
|
arguments: None,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Message::user().with_tool_response("search_1", Ok(vec![])),
|
||||||
|
Message::assistant()
|
||||||
|
.with_text("I need to search more")
|
||||||
|
.with_tool_request(
|
||||||
|
"search_2",
|
||||||
|
Ok(CallToolRequestParam {
|
||||||
|
name: "search".into(),
|
||||||
|
arguments: None,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Message::user().with_tool_response("search_2", Ok(vec![])),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let result = inject_moim(conv, &em).await;
|
||||||
|
let msgs = result.messages();
|
||||||
|
|
||||||
|
assert_eq!(msgs.len(), 6);
|
||||||
|
|
||||||
|
let moim_msg = &msgs[3];
|
||||||
|
let has_moim = moim_msg
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.any(|c| c.as_text().is_some_and(|t| t.contains("<info-msg>")));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
has_moim,
|
||||||
|
"MOIM should be in message before latest assistant message"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ expression: system_prompt
|
|||||||
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
||||||
goose is being developed as an open-source software project.
|
goose is being developed as an open-source software project.
|
||||||
|
|
||||||
The current date is 1970-01-01 00:00:00.
|
|
||||||
|
|
||||||
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
||||||
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
||||||
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
||||||
|
|||||||
-2
@@ -5,8 +5,6 @@ expression: system_prompt
|
|||||||
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
||||||
goose is being developed as an open-source software project.
|
goose is being developed as an open-source software project.
|
||||||
|
|
||||||
The current date is 1970-01-01 00:00:00.
|
|
||||||
|
|
||||||
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
||||||
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
||||||
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
||||||
|
|||||||
-2
@@ -5,8 +5,6 @@ expression: system_prompt
|
|||||||
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
||||||
goose is being developed as an open-source software project.
|
goose is being developed as an open-source software project.
|
||||||
|
|
||||||
The current date is 1970-01-01 00:00:00.
|
|
||||||
|
|
||||||
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
||||||
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
||||||
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
||||||
|
|||||||
@@ -10,13 +10,19 @@ use rmcp::model::{
|
|||||||
ListPromptsResult, ListResourcesResult, ListToolsResult, ProtocolVersion, ReadResourceResult,
|
ListPromptsResult, ListResourcesResult, ListToolsResult, ProtocolVersion, ReadResourceResult,
|
||||||
ServerCapabilities, ServerNotification, Tool, ToolAnnotations, ToolsCapability,
|
ServerCapabilities, ServerNotification, Tool, ToolAnnotations, ToolsCapability,
|
||||||
};
|
};
|
||||||
use rmcp::object;
|
use schemars::{schema_for, JsonSchema};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
pub static EXTENSION_NAME: &str = "todo";
|
pub static EXTENSION_NAME: &str = "todo";
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
struct TodoWriteParams {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TodoClient {
|
pub struct TodoClient {
|
||||||
info: InitializeResult,
|
info: InitializeResult,
|
||||||
context: PlatformExtensionContext,
|
context: PlatformExtensionContext,
|
||||||
@@ -47,16 +53,19 @@ impl TodoClient {
|
|||||||
instructions: Some(indoc! {r#"
|
instructions: Some(indoc! {r#"
|
||||||
Task Management
|
Task Management
|
||||||
|
|
||||||
Use todo_read and todo_write for tasks with 2+ steps, multiple files/components, or uncertain scope.
|
Use todo_write for tasks with 2+ steps, multiple files/components, or uncertain scope.
|
||||||
|
Your TODO content is automatically available in your context.
|
||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
- Start: read → write checklist
|
- Start: write initial checklist
|
||||||
- During: read → update progress
|
- During: update progress
|
||||||
- End: verify all complete
|
- End: verify all complete
|
||||||
|
|
||||||
Warning: todo_write overwrites entirely; always todo_read first (skipping is an error)
|
Warning: todo_write overwrites entirely; always include ALL content you want to keep
|
||||||
|
|
||||||
Keep items short, specific, action-oriented. Not using the todo tools for complex tasks is an error.
|
Keep items short, specific, action-oriented. Not using the todo tool for complex tasks is an error.
|
||||||
|
|
||||||
|
For autonomous work, missing requirements means failure - document all requirements in TODO immediately.
|
||||||
|
|
||||||
Template:
|
Template:
|
||||||
- [ ] Implement feature X
|
- [ ] Implement feature X
|
||||||
@@ -75,24 +84,6 @@ impl TodoClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_read_todo(&self) -> Result<Vec<Content>, String> {
|
|
||||||
if let Some(session_id) = &self.context.session_id {
|
|
||||||
match SessionManager::get_session(session_id, false).await {
|
|
||||||
Ok(metadata) => {
|
|
||||||
let content =
|
|
||||||
extension_data::TodoState::from_extension_data(&metadata.extension_data)
|
|
||||||
.map(|state| state.content)
|
|
||||||
.unwrap_or_default();
|
|
||||||
Ok(vec![Content::text(content)])
|
|
||||||
}
|
|
||||||
Err(_) => Ok(vec![Content::text(String::new())]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let content = self.fallback_content.read().await;
|
|
||||||
Ok(vec![Content::text(content.clone())])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_write_todo(
|
async fn handle_write_todo(
|
||||||
&self,
|
&self,
|
||||||
arguments: Option<JsonObject>,
|
arguments: Option<JsonObject>,
|
||||||
@@ -154,61 +145,32 @@ impl TodoClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_tools() -> Vec<Tool> {
|
fn get_tools() -> Vec<Tool> {
|
||||||
vec![
|
let schema = schema_for!(TodoWriteParams);
|
||||||
Tool::new(
|
let schema_value =
|
||||||
"todo_read".to_string(),
|
serde_json::to_value(schema).expect("Failed to serialize TodoWriteParams schema");
|
||||||
indoc! {r#"
|
|
||||||
Read the entire TODO file content.
|
|
||||||
|
|
||||||
This tool reads the complete TODO file and returns its content as a string.
|
vec![Tool::new(
|
||||||
Use this to view current tasks, notes, and any other information stored in the TODO file.
|
"todo_write".to_string(),
|
||||||
|
indoc! {r#"
|
||||||
|
Overwrite the entire TODO content.
|
||||||
|
|
||||||
The tool will return an error if the TODO file doesn't exist or cannot be read.
|
The content persists across conversation turns and compaction. Use this for:
|
||||||
"#}.to_string(),
|
- Task tracking and progress updates
|
||||||
object!({
|
- Important notes and reminders
|
||||||
"type": "object",
|
|
||||||
"properties": {},
|
|
||||||
"required": []
|
|
||||||
}),
|
|
||||||
).annotate(ToolAnnotations {
|
|
||||||
title: Some("Read TODO file".to_string()),
|
|
||||||
read_only_hint: Some(true),
|
|
||||||
destructive_hint: Some(false),
|
|
||||||
idempotent_hint: Some(true),
|
|
||||||
open_world_hint: Some(false),
|
|
||||||
}),
|
|
||||||
Tool::new(
|
|
||||||
"todo_write".to_string(),
|
|
||||||
indoc! {r#"
|
|
||||||
Write or overwrite the entire TODO file content.
|
|
||||||
|
|
||||||
This tool replaces the complete TODO file content with the provided string.
|
WARNING: This operation completely replaces the existing content. Always include
|
||||||
Use this to update tasks, add new items, or reorganize the TODO file.
|
|
||||||
|
|
||||||
WARNING: This operation completely replaces the file content. Make sure to include
|
|
||||||
all content you want to keep, not just the changes.
|
all content you want to keep, not just the changes.
|
||||||
|
"#}
|
||||||
The tool will create the TODO file if it doesn't exist, or overwrite it if it does.
|
.to_string(),
|
||||||
Returns an error if the file cannot be written due to permissions or other I/O issues.
|
schema_value.as_object().unwrap().clone(),
|
||||||
"#}.to_string(),
|
)
|
||||||
object!({
|
.annotate(ToolAnnotations {
|
||||||
"type": "object",
|
title: Some("Write TODO".to_string()),
|
||||||
"properties": {
|
read_only_hint: Some(false),
|
||||||
"content": {
|
destructive_hint: Some(true),
|
||||||
"type": "string",
|
idempotent_hint: Some(false),
|
||||||
"description": "The TODO list content to save"
|
open_world_hint: Some(false),
|
||||||
}
|
})]
|
||||||
},
|
|
||||||
"required": ["content"]
|
|
||||||
}),
|
|
||||||
).annotate(ToolAnnotations {
|
|
||||||
title: Some("Write TODO file".to_string()),
|
|
||||||
read_only_hint: Some(false),
|
|
||||||
destructive_hint: Some(true),
|
|
||||||
idempotent_hint: Some(false),
|
|
||||||
open_world_hint: Some(false),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +210,6 @@ impl McpClientTrait for TodoClient {
|
|||||||
_cancellation_token: CancellationToken,
|
_cancellation_token: CancellationToken,
|
||||||
) -> Result<CallToolResult, Error> {
|
) -> Result<CallToolResult, Error> {
|
||||||
let content = match name {
|
let content = match name {
|
||||||
"todo_read" => self.handle_read_todo().await,
|
|
||||||
"todo_write" => self.handle_write_todo(arguments).await,
|
"todo_write" => self.handle_write_todo(arguments).await,
|
||||||
_ => Err(format!("Unknown tool: {}", name)),
|
_ => Err(format!("Unknown tool: {}", name)),
|
||||||
};
|
};
|
||||||
@@ -286,4 +247,16 @@ impl McpClientTrait for TodoClient {
|
|||||||
fn get_info(&self) -> Option<&InitializeResult> {
|
fn get_info(&self) -> Option<&InitializeResult> {
|
||||||
Some(&self.info)
|
Some(&self.info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_moim(&self) -> Option<String> {
|
||||||
|
let session_id = self.context.session_id.as_ref()?;
|
||||||
|
let metadata = SessionManager::get_session(session_id, false).await.ok()?;
|
||||||
|
let state = extension_data::TodoState::from_extension_data(&metadata.extension_data)?;
|
||||||
|
|
||||||
|
if state.content.trim().is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(format!("Current tasks and notes:\n{}\n", state.content))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
You are a specialized subagent within the goose AI framework, created by Block. You were spawned by the main goose agent to handle a specific task efficiently. The current date is {{current_date_time}}.
|
You are a specialized subagent within the goose AI framework, created by Block. You were spawned by the main goose agent to handle a specific task efficiently.
|
||||||
|
|
||||||
# Your Role
|
# Your Role
|
||||||
You are an autonomous subagent with these characteristics:
|
You are an autonomous subagent with these characteristics:
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.
|
||||||
goose is being developed as an open-source software project.
|
goose is being developed as an open-source software project.
|
||||||
|
|
||||||
The current date is {{current_date_time}}.
|
|
||||||
|
|
||||||
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,
|
||||||
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).
|
||||||
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10
|
||||||
|
|||||||
Reference in New Issue
Block a user