Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
563fbe2
comiples
katzdave Jul 18, 2025
aa43f09
tests
katzdave Jul 18, 2025
9ea702b
context usage fix
katzdave Jul 18, 2025
6bbba20
change overhead
katzdave Jul 18, 2025
ff5c9a2
shrink works
katzdave Jul 18, 2025
75c074b
merge
katzdave Jul 21, 2025
52099ce
new prompt
katzdave Jul 21, 2025
f8b37d0
move token counter
katzdave Jul 21, 2025
e83cadc
reset changes to mod.rs and agent.rs to focus on summarization algorithm
katzdave Jul 21, 2025
324c511
rm old summarizer
katzdave Jul 22, 2025
5546bef
shrink token counting
katzdave Jul 22, 2025
dc668bb
reset token counting
katzdave Jul 22, 2025
7b6f17e
fmt
katzdave Jul 22, 2025
c4ec13f
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 22, 2025
e55dc40
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 22, 2025
f71fe31
fix test
katzdave Jul 22, 2025
ed54252
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 23, 2025
970b197
appending to pr: ads auto summarize to one shot (#3600)
michaelneale Jul 24, 2025
0f37623
rm tool call removal fns
katzdave Jul 24, 2025
fcf0f27
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 24, 2025
ef432b5
one more unused symbol
katzdave Jul 24, 2025
624e3eb
fmt
katzdave Jul 24, 2025
caf4557
split compaction into check fn
katzdave Jul 24, 2025
1c27b65
refactor into agent reply
katzdave Jul 24, 2025
ad7ca3d
add logging
katzdave Jul 25, 2025
94dc7a0
fix token reduction
katzdave Jul 25, 2025
46a8ec7
merge
katzdave Jul 25, 2025
0aa079a
fmt tidy up
michaelneale Jul 25, 2025
23162e4
import the right tool, don't rely on wildcard
michaelneale Jul 25, 2025
8342d35
rm debug logs
katzdave Jul 25, 2025
55dd73a
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 25, 2025
8e04278
merge
katzdave Jul 25, 2025
4b12c2b
rm extra file + clean comments
katzdave Jul 25, 2025
fc62ac9
merge conflict
katzdave Jul 28, 2025
97fa0f8
fmt
katzdave Jul 28, 2025
b233c74
autocompact splice last message
katzdave Jul 28, 2025
1a1733e
fix threshold
katzdave Jul 28, 2025
2966295
fmt
katzdave Jul 28, 2025
8df19c5
unused
katzdave Jul 28, 2025
d8f07ae
rm stray files
katzdave Jul 28, 2025
c7c2dd8
Merge branch 'main' into dkatz/goose-compact2
michaelneale Jul 29, 2025
158b5d0
merge
katzdave Jul 29, 2025
719086c
Merge branch 'dkatz/goose-compact2' of github.com:block/goose into dk…
katzdave Jul 29, 2025
985dfcd
rm noise
katzdave Jul 29, 2025
a7e68b6
replace with shorter summary
katzdave Jul 29, 2025
35179ac
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 29, 2025
739eb0b
fix test
katzdave Jul 29, 2025
edb594d
Resolve merge conflict in agent.rs by extracting auto_compact logic t…
katzdave Jul 30, 2025
168f332
merge fix pt 2
katzdave Jul 30, 2025
585a260
fmt
katzdave Jul 30, 2025
2ed4fbf
rename
katzdave Jul 30, 2025
da96097
prompt swap
katzdave Jul 30, 2025
a3e722d
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 30, 2025
a18db4f
rm old summary prompt
katzdave Jul 30, 2025
fb7f2c3
rm stray file
katzdave Jul 31, 2025
f114024
more stray test changes
katzdave Jul 31, 2025
75b58a4
Merge branch 'main' of github.com:block/goose into dkatz/goose-compact2
katzdave Jul 31, 2025
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
33 changes: 30 additions & 3 deletions crates/goose-cli/src/commands/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,8 @@ async fn process_message_streaming(
// Create a user message
let user_message = GooseMessage::user().with_text(content.clone());

// Get existing messages from session and add the new user message
let mut messages = {
// Messages will be auto-compacted in agent.reply() if needed
let messages = {
let mut session_msgs = session_messages.lock().await;
session_msgs.push(user_message.clone());
session_msgs.clone()
Expand Down Expand Up @@ -618,14 +618,41 @@ async fn process_message_streaming(
// TODO: Implement proper UI for context handling
let (summarized_messages, _) =
agent.summarize_context(&messages).await?;
messages = summarized_messages;
{
let mut session_msgs = session_messages.lock().await;
*session_msgs = summarized_messages;
}
}
_ => {
// Handle other message types as needed
}
}
}
}
Ok(AgentEvent::HistoryReplaced(new_messages)) => {
// Replace the session's message history with the compacted messages
{
let mut session_msgs = session_messages.lock().await;
*session_msgs = new_messages;
}

// Persist the updated messages to the JSONL file
let current_messages = {
let session_msgs = session_messages.lock().await;
session_msgs.clone()
};

if let Err(e) = session::persist_messages(
&session_file,
&current_messages,
None, // No provider needed for persisting
working_dir.clone(),
)
.await
{
error!("Failed to persist compacted messages: {}", e);
}
}
Ok(AgentEvent::McpNotification(_notification)) => {
// Handle MCP notifications if needed
// For now, we'll just log them
Expand Down
20 changes: 20 additions & 0 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ impl Session {
}

async fn process_agent_response(&mut self, interactive: bool) -> Result<()> {
// Messages will be auto-compacted in agent.reply() if needed
let cancel_token = CancellationToken::new();
let cancel_token_clone = cancel_token.clone();

Expand Down Expand Up @@ -1140,6 +1141,25 @@ impl Session {
_ => (),
}
}
Some(Ok(AgentEvent::HistoryReplaced(new_messages))) => {
// Replace the session's message history with the compacted messages
self.messages = new_messages;

// Persist the updated messages to the session file
if let Some(session_file) = &self.session_file {
let provider = self.agent.provider().await.ok();
let working_dir = std::env::current_dir().ok();
if let Err(e) = session::persist_messages_with_schedule_id(
session_file,
&self.messages,
provider,
self.scheduled_job_id.clone(),
working_dir,
).await {
eprintln!("Failed to persist compacted messages: {}", e);
}
}
}
Some(Ok(AgentEvent::ModelChange { model, mode })) => {
// Log model change if in debug mode
if self.debug {
Expand Down
15 changes: 14 additions & 1 deletion crates/goose-server/src/routes/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,15 @@ async fn reply_handler(
retry_config: None,
};

// Messages will be auto-compacted in agent.reply() if needed
let messages_to_process = messages.clone();

let mut stream = match agent
.reply(&messages, Some(session_config), Some(task_cancel.clone()))
.reply(
&messages_to_process,
Some(session_config),
Some(task_cancel.clone()),
)
.await
{
Ok(stream) => stream,
Expand Down Expand Up @@ -215,6 +222,12 @@ async fn reply_handler(
break;
}
}
Ok(Some(Ok(AgentEvent::HistoryReplaced(new_messages)))) => {
// Replace the message history with the compacted messages
all_messages = new_messages;
// Note: We don't send this as a stream event since it's an internal operation
// The client will see the compaction notification message that was sent before this event
}
Ok(Some(Ok(AgentEvent::ModelChange { model, mode }))) => {
if let Err(e) = stream_event(MessageEvent::ModelChange { model, mode }, &tx).await {
tracing::error!("Error sending model change through channel: {}", e);
Expand Down
74 changes: 70 additions & 4 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::agents::tool_router_index_manager::ToolRouterIndexManager;
use crate::agents::types::SessionConfig;
use crate::agents::types::{FrontendTool, ToolResultReceiver};
use crate::config::{Config, ExtensionConfigManager, PermissionManager};
use crate::context_mgmt::auto_compact;
use crate::message::{push_message, Message, ToolRequest};
use crate::permission::permission_judge::{check_tool_permissions, PermissionCheckResult};
use crate::permission::PermissionConfirmation;
Expand Down Expand Up @@ -102,6 +103,7 @@ pub enum AgentEvent {
Message(Message),
McpNotification((String, ServerNotification)),
ModelChange { model: String, mode: String },
HistoryReplaced(Vec<Message>),
}

impl Default for Agent {
Expand Down Expand Up @@ -746,16 +748,81 @@ impl Agent {
}
}

/// Handle auto-compaction logic and return compacted messages if needed
async fn handle_auto_compaction(
&self,
messages: &[Message],
) -> Result<Option<(Vec<Message>, String)>> {
let compact_result = auto_compact::check_and_compact_messages(self, messages, None).await?;

if compact_result.compacted {
let compacted_messages = compact_result.messages;

// Create compaction notification message
let compaction_msg = if let (Some(before), Some(after)) =
(compact_result.tokens_before, compact_result.tokens_after)
{
format!(
"Auto-compacted context: {} → {} tokens ({:.0}% reduction)\n\n",
before,
after,
(1.0 - (after as f64 / before as f64)) * 100.0
)
} else {
"Auto-compacted context to reduce token usage\n\n".to_string()
};

return Ok(Some((compacted_messages, compaction_msg)));
}

Ok(None)
}

#[instrument(skip(self, unfixed_messages, session), fields(user_message))]
pub async fn reply(
&self,
unfixed_messages: &[Message],
session: Option<SessionConfig>,
cancel_token: Option<CancellationToken>,
) -> Result<BoxStream<'_, Result<AgentEvent>>> {
let context = self
.prepare_reply_context(unfixed_messages, &session)
.await?;
// Handle auto-compaction before processing
let (messages, compaction_msg) = match self.handle_auto_compaction(unfixed_messages).await?
{
Some((compacted_messages, msg)) => (compacted_messages, Some(msg)),
None => {
let context = self
.prepare_reply_context(unfixed_messages, &session)
.await?;
(context.messages, None)
}
};

// If we compacted, yield the compaction message and history replacement event
if let Some(compaction_msg) = compaction_msg {
return Ok(Box::pin(async_stream::try_stream! {
yield AgentEvent::Message(Message::assistant().with_text(compaction_msg));
yield AgentEvent::HistoryReplaced(messages.clone());

// Continue with normal reply processing using compacted messages
let mut reply_stream = self.reply_internal(&messages, session, cancel_token).await?;
while let Some(event) = reply_stream.next().await {
yield event?;
}
}));
}

// No compaction needed, proceed with normal processing
self.reply_internal(&messages, session, cancel_token).await
}

/// Main reply method that handles the actual agent processing
async fn reply_internal(
&self,
messages: &[Message],
session: Option<SessionConfig>,
cancel_token: Option<CancellationToken>,
) -> Result<BoxStream<'_, Result<AgentEvent>>> {
let context = self.prepare_reply_context(messages, &session).await?;
let ReplyContext {
mut messages,
mut tools,
Expand All @@ -765,7 +832,6 @@ impl Agent {
initial_messages,
config,
} = context;

let reply_span = tracing::Span::current();
self.reset_retry_attempts().await;

Expand Down
Loading
Loading