From 5f4b7cc100b0ee9b44cadd3171f0707b84b615ac Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Fri, 14 Aug 2026 13:33:16 +0000 Subject: [PATCH] create the goose-agent crate with the unrolled agent loop state machine (#11216) Co-authored-by: Alex Hancock --- Cargo.lock | 29 ++++++++++++++----- crates/goose-agent/Cargo.toml | 22 ++++++++++++++ .../src/agents => goose-agent/src}/events.rs | 10 ++++--- crates/goose-agent/src/lib.rs | 3 ++ .../src}/machine.rs | 6 ++-- .../src}/operation.rs | 6 ++-- crates/goose-context-management/Cargo.toml | 4 +-- crates/goose-download-manager/Cargo.toml | 2 +- crates/goose-local-inference/Cargo.toml | 8 ++--- crates/goose-provider-types/Cargo.toml | 2 +- crates/goose-providers/Cargo.toml | 6 ++-- crates/goose-sdk-types/Cargo.toml | 2 +- crates/goose-sdk/Cargo.toml | 8 ++--- crates/goose-sdk/justfile | 6 ++++ crates/goose-sdk/python/pyproject.toml | 2 +- crates/goose/Cargo.toml | 1 + crates/goose/src/agents/mod.rs | 3 +- .../goose/src/agents/state_machine/effects.rs | 2 +- crates/goose/src/agents/state_machine/mod.rs | 8 ++--- .../goose/src/agents/state_machine/session.rs | 6 ++-- 20 files changed, 91 insertions(+), 45 deletions(-) create mode 100644 crates/goose-agent/Cargo.toml rename crates/{goose/src/agents => goose-agent/src}/events.rs (64%) create mode 100644 crates/goose-agent/src/lib.rs rename crates/{goose/src/agents/state_machine => goose-agent/src}/machine.rs (96%) rename crates/{goose/src/agents/state_machine => goose-agent/src}/operation.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index 69b480cf3..6b06f73a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4967,6 +4967,7 @@ dependencies = [ "futures", "gethostname", "goose-acp-macros", + "goose-agent", "goose-context-management", "goose-download-manager", "goose-mcp", @@ -5073,6 +5074,20 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "goose-agent" +version = "0.1.0-alpha.6" +dependencies = [ + "anyhow", + "async-trait", + "goose-provider-types", + "rmcp 3.0.0", + "serde_json", + "tokio", + "tokio-util", + "uuid", +] + [[package]] name = "goose-cli" version = "1.46.0" @@ -5133,7 +5148,7 @@ dependencies = [ [[package]] name = "goose-context-management" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "anyhow", "async-trait", @@ -5149,7 +5164,7 @@ dependencies = [ [[package]] name = "goose-download-manager" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "anyhow", "once_cell", @@ -5161,7 +5176,7 @@ dependencies = [ [[package]] name = "goose-local-inference" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "anyhow", "async-stream", @@ -5225,7 +5240,7 @@ dependencies = [ [[package]] name = "goose-provider-types" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "anyhow", "async-stream", @@ -5254,7 +5269,7 @@ dependencies = [ [[package]] name = "goose-providers" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "anyhow", "async-stream", @@ -5286,7 +5301,7 @@ dependencies = [ [[package]] name = "goose-sdk" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "agent-client-protocol", "agent-client-protocol-schema", @@ -5306,7 +5321,7 @@ dependencies = [ [[package]] name = "goose-sdk-types" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" dependencies = [ "agent-client-protocol", "agent-client-protocol-schema", diff --git a/crates/goose-agent/Cargo.toml b/crates/goose-agent/Cargo.toml new file mode 100644 index 000000000..ab7e88964 --- /dev/null +++ b/crates/goose-agent/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "goose-agent" +version = "0.1.0-alpha.6" +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +description = "The GDK's Agent Loop" + +[lints] +workspace = true + +[dependencies] +anyhow = { workspace = true } +tokio = { workspace = true, features = ["rt-multi-thread", "macros", "sync", "time", "fs", "process", "net", "signal", "io-std", "io-util"] } +tokio-util = { workspace = true, features = ["compat"] } +async-trait = { workspace = true } +goose-provider-types = { version = "0.1.0-alpha.6", path = "../goose-provider-types", default-features = false } +rmcp = { workspace = true } +uuid = { workspace = true } +serde_json = { workspace = true } diff --git a/crates/goose/src/agents/events.rs b/crates/goose-agent/src/events.rs similarity index 64% rename from crates/goose/src/agents/events.rs rename to crates/goose-agent/src/events.rs index 99bbbd505..8c2c3782c 100644 --- a/crates/goose/src/agents/events.rs +++ b/crates/goose-agent/src/events.rs @@ -1,12 +1,14 @@ +use goose_provider_types::conversation::{ + message::{Message, MessageUsage}, + token_usage::ProviderUsage, + Conversation, +}; use rmcp::model::ServerNotification; -use crate::conversation::message::{Message, MessageUsage}; -use crate::conversation::Conversation; - #[derive(Clone, Debug)] pub enum AgentEvent { Message(Message), - Usage(crate::providers::base::ProviderUsage), + Usage(ProviderUsage), MessageUsage { message_id: Option, usage: MessageUsage, diff --git a/crates/goose-agent/src/lib.rs b/crates/goose-agent/src/lib.rs new file mode 100644 index 000000000..1336ff9a9 --- /dev/null +++ b/crates/goose-agent/src/lib.rs @@ -0,0 +1,3 @@ +pub mod events; +pub mod machine; +pub mod operation; diff --git a/crates/goose/src/agents/state_machine/machine.rs b/crates/goose-agent/src/machine.rs similarity index 96% rename from crates/goose/src/agents/state_machine/machine.rs rename to crates/goose-agent/src/machine.rs index fceeb59e7..b4dfbe0e9 100644 --- a/crates/goose/src/agents/state_machine/machine.rs +++ b/crates/goose-agent/src/machine.rs @@ -4,11 +4,11 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use tokio_util::sync::CancellationToken; -use crate::agents::state_machine::operation::{ +use crate::operation::{ ConversationEffect, Emitter, Inference, InferenceInput, MachineEffect, Operation, OperationFuture, OperationResult, StepResult, }; -use crate::conversation::Conversation; +use goose_provider_types::conversation::Conversation; pub trait MachineSession: Send + Sync { fn id(&self) -> &str; @@ -26,7 +26,7 @@ pub trait EffectHandler: Send + Sync { } pub trait EffectUsage: Send + Sync { - fn usage(&self, _effect: &E) -> Option { + fn usage(&self, _effect: &E) -> Option { None } } diff --git a/crates/goose/src/agents/state_machine/operation.rs b/crates/goose-agent/src/operation.rs similarity index 97% rename from crates/goose/src/agents/state_machine/operation.rs rename to crates/goose-agent/src/operation.rs index f903ef10c..acd27d89e 100644 --- a/crates/goose/src/agents/state_machine/operation.rs +++ b/crates/goose-agent/src/operation.rs @@ -5,9 +5,9 @@ use std::pin::Pin; use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; -use crate::agents::AgentEvent; -use crate::conversation::message::{Message, MessageContent, MessageErrorKind}; -use crate::conversation::{effective_role, Conversation, EffectiveRole}; +use crate::events::AgentEvent; +use goose_provider_types::conversation::message::{Message, MessageContent, MessageErrorKind}; +use goose_provider_types::conversation::{effective_role, Conversation, EffectiveRole}; use rmcp::model::Tool; pub type OperationFuture<'a, T> = Pin + Send + 'a>>; diff --git a/crates/goose-context-management/Cargo.toml b/crates/goose-context-management/Cargo.toml index 2725dddff..3281a19e9 100644 --- a/crates/goose-context-management/Cargo.toml +++ b/crates/goose-context-management/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-context-management" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true @@ -12,7 +12,7 @@ description = "Conversation compaction for Goose" workspace = true [dependencies] -goose-providers = { version = "0.1.0-alpha.5", path = "../goose-providers", features = ["rustls-tls"] } +goose-providers = { version = "0.1.0-alpha.6", path = "../goose-providers", features = ["rustls-tls"] } anyhow = { workspace = true } async-trait = { workspace = true } include_dir = { workspace = true } diff --git a/crates/goose-download-manager/Cargo.toml b/crates/goose-download-manager/Cargo.toml index 39b2c29b1..a31c81bf2 100644 --- a/crates/goose-download-manager/Cargo.toml +++ b/crates/goose-download-manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-download-manager" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true diff --git a/crates/goose-local-inference/Cargo.toml b/crates/goose-local-inference/Cargo.toml index a4ecd640a..bc60fc257 100644 --- a/crates/goose-local-inference/Cargo.toml +++ b/crates/goose-local-inference/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-local-inference" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true @@ -27,9 +27,9 @@ etcetera = { workspace = true } encoding_rs = { version = "0.8.35", default-features = false } fs2 = { workspace = true } futures = { workspace = true } -goose-download-manager = { version = "0.1.0-alpha.5", path = "../goose-download-manager" } -goose-provider-types = { version = "0.1.0-alpha.5", path = "../goose-provider-types", default-features = false } -goose-sdk-types = { version = "0.1.0-alpha.5", path = "../goose-sdk-types", default-features = false } +goose-download-manager = { version = "0.1.0-alpha.6", path = "../goose-download-manager" } +goose-provider-types = { version = "0.1.0-alpha.6", path = "../goose-provider-types", default-features = false } +goose-sdk-types = { version = "0.1.0-alpha.6", path = "../goose-sdk-types", default-features = false } hf-hub = { version = "1.0.0-rc.1", default-features = false } include_dir = { workspace = true } llama-cpp-2 = { workspace = true } diff --git a/crates/goose-provider-types/Cargo.toml b/crates/goose-provider-types/Cargo.toml index 76b8b3821..4c5b4aaa4 100644 --- a/crates/goose-provider-types/Cargo.toml +++ b/crates/goose-provider-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-provider-types" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true diff --git a/crates/goose-providers/Cargo.toml b/crates/goose-providers/Cargo.toml index ab231235b..b88d2685d 100644 --- a/crates/goose-providers/Cargo.toml +++ b/crates/goose-providers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-providers" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true @@ -35,8 +35,8 @@ anyhow = { workspace = true } async-stream = { workspace = true } chrono = { workspace = true } futures = { workspace = true } -goose-provider-types = { version = "0.1.0-alpha.5", path = "../goose-provider-types", default-features = false } -goose-local-inference = { version = "0.1.0-alpha.5", path = "../goose-local-inference", default-features = false, optional = true } +goose-provider-types = { version = "0.1.0-alpha.6", path = "../goose-provider-types", default-features = false } +goose-local-inference = { version = "0.1.0-alpha.6", path = "../goose-local-inference", default-features = false, optional = true } reqwest = { workspace = true } rmcp = { workspace = true, features = ["server", "macros"] } serde = { workspace = true } diff --git a/crates/goose-sdk-types/Cargo.toml b/crates/goose-sdk-types/Cargo.toml index 95bcf5fd6..5a7b9530a 100644 --- a/crates/goose-sdk-types/Cargo.toml +++ b/crates/goose-sdk-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-sdk-types" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true diff --git a/crates/goose-sdk/Cargo.toml b/crates/goose-sdk/Cargo.toml index 817f47650..5126ef891 100644 --- a/crates/goose-sdk/Cargo.toml +++ b/crates/goose-sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "goose-sdk" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" edition.workspace = true rust-version.workspace = true authors.workspace = true @@ -33,14 +33,14 @@ uniffi = [ ] [dependencies] -goose-sdk-types = { version = "0.1.0-alpha.5", path = "../goose-sdk-types" } +goose-sdk-types = { version = "0.1.0-alpha.6", path = "../goose-sdk-types" } agent-client-protocol = { workspace = true, features = ["unstable"] } agent-client-protocol-schema = { workspace = true } uniffi = { version = "0.32", features = ["cli"], optional = true } thiserror = { version = "2", optional = true } -goose-providers = { version = "0.1.0-alpha.5", path = "../goose-providers", features = ["rustls-tls"], optional = true } -goose-context-management = { version = "0.1.0-alpha.5", path = "../goose-context-management", optional = true } +goose-providers = { version = "0.1.0-alpha.6", path = "../goose-providers", features = ["rustls-tls"], optional = true } +goose-context-management = { version = "0.1.0-alpha.6", path = "../goose-context-management", optional = true } futures = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } tokio = { workspace = true, features = ["rt-multi-thread", "sync"], optional = true } diff --git a/crates/goose-sdk/justfile b/crates/goose-sdk/justfile index 404d0b6db..6e4078e37 100644 --- a/crates/goose-sdk/justfile +++ b/crates/goose-sdk/justfile @@ -104,6 +104,8 @@ crates-publish dry_run="true": goose-download-manager \ goose-local-inference \ goose-providers \ + goose-agent \ + goose-context-management \ goose-sdk; do \ cargo publish -p "$crate" $dry_run_flag --allow-dirty; \ done @@ -130,6 +132,8 @@ bump-version rust_version: "goose-download-manager", "goose-local-inference", "goose-providers", + "goose-agent", + "goose-context-management", "goose-sdk", ] crate_paths = {name: Path("crates") / name / "Cargo.toml" for name in crate_names} @@ -150,6 +154,8 @@ bump-version rust_version: "goose-download-manager", "goose-local-inference", "goose-providers", + "goose-agent", + "goose-context-management", ] for path in crate_paths.values(): text = path.read_text() diff --git a/crates/goose-sdk/python/pyproject.toml b/crates/goose-sdk/python/pyproject.toml index 4a97beda7..4b5d44336 100644 --- a/crates/goose-sdk/python/pyproject.toml +++ b/crates/goose-sdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "goose-sdk" -version = "0.1.0a5" +version = "0.1.0a6" description = "Python bindings for the Goose SDK" readme = "README.md" requires-python = ">=3.9" diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index 476872090..b487b46fa 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -139,6 +139,7 @@ goose-context-management = { path = "../goose-context-management" } goose-providers = { path = "../goose-providers", default-features = false } goose-download-manager = { path = "../goose-download-manager" } goose-sdk-types = { path = "../goose-sdk-types" } +goose-agent = { path = "../goose-agent" } rand = { workspace = true } tokio-cron-scheduler = { version = "0.15", default-features = false } urlencoding = { workspace = true } diff --git a/crates/goose/src/agents/mod.rs b/crates/goose/src/agents/mod.rs index 4fe5c2e59..11a2fad7f 100644 --- a/crates/goose/src/agents/mod.rs +++ b/crates/goose/src/agents/mod.rs @@ -1,6 +1,5 @@ mod agent; pub mod container; -mod events; pub mod execute_commands; pub mod extension; pub mod extension_malware_check; @@ -28,10 +27,10 @@ pub mod validate_extensions; pub use agent::{Agent, AgentConfig, ExtensionLoadResult, GoosePlatform}; pub use container::Container; -pub use events::AgentEvent; pub use execute_commands::COMPACT_TRIGGERS; pub use extension::{ExtensionConfig, ExtensionError}; pub use extension_manager::ExtensionManager; +pub use goose_agent::events::AgentEvent; pub use prompt_manager::PromptManager; pub use schedule_tool::ScheduleTool; pub use subagent_handler::SUBAGENT_TOOL_REQUEST_TYPE; diff --git a/crates/goose/src/agents/state_machine/effects.rs b/crates/goose/src/agents/state_machine/effects.rs index 8d8960c11..bc2dbbdd5 100644 --- a/crates/goose/src/agents/state_machine/effects.rs +++ b/crates/goose/src/agents/state_machine/effects.rs @@ -1,9 +1,9 @@ -use crate::agents::state_machine::operation::{ConversationEffect, MachineEffect}; use crate::conversation::message::Message; use crate::conversation::Conversation; use crate::providers::base::ProviderUsage; use crate::recipe::Recipe; use crate::session::ExtensionData; +use goose_agent::operation::{ConversationEffect, MachineEffect}; pub enum GooseEffect { Conversation(ConversationEffect), diff --git a/crates/goose/src/agents/state_machine/mod.rs b/crates/goose/src/agents/state_machine/mod.rs index 10919cd36..fe6825c5a 100644 --- a/crates/goose/src/agents/state_machine/mod.rs +++ b/crates/goose/src/agents/state_machine/mod.rs @@ -6,8 +6,6 @@ //! configuration is part of `Agent::reply`, not the state-machine protocol. mod effects; -mod machine; -mod operation; mod ops_bang_shell; mod ops_compaction; mod ops_doctor; @@ -34,8 +32,10 @@ mod usage; mod tests; pub use effects::GooseEffect; -pub use machine::{EffectHandler, EffectUsage, MachineSession, SessionLoader, StateMachine, Step}; -pub use operation::{ +pub use goose_agent::machine::{ + EffectHandler, EffectUsage, MachineSession, SessionLoader, StateMachine, Step, +}; +pub use goose_agent::operation::{ applied, assistant_turn_count, ends_turn, last_effective_role, messages_since_kickoff, not_applicable, trailing_error, yielded, yielded_with, ConversationEffect, Emitter, Inference, InferenceInput, MachineEffect, Operation, OperationResult, SlashCommand, StepResult, diff --git a/crates/goose/src/agents/state_machine/session.rs b/crates/goose/src/agents/state_machine/session.rs index c31f68870..195c2b435 100644 --- a/crates/goose/src/agents/state_machine/session.rs +++ b/crates/goose/src/agents/state_machine/session.rs @@ -2,14 +2,12 @@ use anyhow::Result; use async_trait::async_trait; use crate::agents::state_machine::effects::GooseEffect; -use crate::agents::state_machine::machine::{ - EffectHandler, EffectUsage, MachineSession, SessionLoader, -}; -use crate::agents::state_machine::operation::{ConversationEffect, Emitter, MachineEffect}; use crate::agents::state_machine::usage; use crate::agents::AgentEvent; use crate::conversation::Conversation; use crate::session::{Session, SessionManager}; +use goose_agent::machine::{EffectHandler, EffectUsage, MachineSession, SessionLoader}; +use goose_agent::operation::{ConversationEffect, Emitter, MachineEffect}; impl MachineSession for Session { fn id(&self) -> &str {