From bbd32413c6f3ab2c2142378d6c86f71fff9d70d1 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Tue, 9 Sep 2025 14:03:16 -0400 Subject: [PATCH 1/6] chore(release): release version 1.8.0 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- ui/desktop/package-lock.json | 4 ++-- ui/desktop/package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7268ed204f01..9551b29d80f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2636,7 +2636,7 @@ dependencies = [ [[package]] name = "goose" -version = "1.7.0" +version = "1.8.0" dependencies = [ "ahash", "anyhow", @@ -2710,7 +2710,7 @@ dependencies = [ [[package]] name = "goose-bench" -version = "1.7.0" +version = "1.8.0" dependencies = [ "anyhow", "async-trait", @@ -2733,7 +2733,7 @@ dependencies = [ [[package]] name = "goose-cli" -version = "1.7.0" +version = "1.8.0" dependencies = [ "agent-client-protocol", "anstream", @@ -2787,7 +2787,7 @@ dependencies = [ [[package]] name = "goose-mcp" -version = "1.7.0" +version = "1.8.0" dependencies = [ "anyhow", "async-trait", @@ -2838,7 +2838,7 @@ dependencies = [ [[package]] name = "goose-server" -version = "1.7.0" +version = "1.8.0" dependencies = [ "anyhow", "async-trait", @@ -2877,7 +2877,7 @@ dependencies = [ [[package]] name = "goose-test" -version = "1.7.0" +version = "1.8.0" dependencies = [ "clap", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index ff90733f4c9f..ec3aa42bd292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "1.7.0" +version = "1.8.0" authors = ["Block "] license = "Apache-2.0" repository = "https://github.com/block/goose" diff --git a/ui/desktop/package-lock.json b/ui/desktop/package-lock.json index 7fcf091b689c..6d59e48ceada 100644 --- a/ui/desktop/package-lock.json +++ b/ui/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "goose-app", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "goose-app", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "@ai-sdk/openai": "^2.0.14", diff --git a/ui/desktop/package.json b/ui/desktop/package.json index b0b5a4af7cbf..b95f7757bf25 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -1,7 +1,7 @@ { "name": "goose-app", "productName": "Goose", - "version": "1.7.0", + "version": "1.8.0", "description": "Goose App", "engines": { "node": "^22.17.1" From e9727f2826bfa148a8b6f683e33af150aa5825c3 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Tue, 9 Sep 2025 14:08:44 -0400 Subject: [PATCH 2/6] chore: OpenAPI version bump --- ui/desktop/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 46e18a0fd2a4..6e5b017a6710 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "1.7.0" + "version": "1.8.0" }, "paths": { "/agent/add_sub_recipes": { From 2692603f493c5bf9b47484aa1f304f3b9f667563 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Wed, 10 Sep 2025 13:27:06 -0400 Subject: [PATCH 3/6] Send the secret with decodeRecipe (#4597) --- ui/desktop/src/main.ts | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index dd3b986ad766..bab533327397 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -51,30 +51,18 @@ import { import { UPDATES_ENABLED } from './updates'; import { Recipe } from './recipe'; import './utils/recipeHash'; +import { decodeRecipe } from './api/sdk.gen'; -// API URL constructor for main process before window is ready -function getApiUrlMain(endpoint: string, dynamicPort: number): string { - const host = process.env.GOOSE_API_HOST || 'http://127.0.0.1'; - const port = dynamicPort || process.env.GOOSE_PORT; - const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; - return `${host}:${port}${cleanEndpoint}`; -} - -// When opening the app with a deeplink, the window is still initializing so we have to duplicate some window dependant logic here. -async function decodeRecipeMain(deeplink: string, port: number): Promise { +async function decodeRecipeMain(deeplink: string): Promise { try { - const response = await fetch(getApiUrlMain('/recipes/decode', port), { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ deeplink }), - }); - - if (response.ok) { - const data = await response.json(); - return data.recipe; - } - } catch { - console.error('Failed to decode recipe'); + return ( + await decodeRecipe({ + throwOnError: true, + body: { deeplink }, + }) + ).data.recipe; + } catch (e) { + console.error('Failed to decode recipe:', e); } return null; } @@ -758,7 +746,7 @@ const createChat = async ( console.log('[Main] Starting background recipe decoding for:', recipeDeeplink); // Decode recipe asynchronously after window is created - decodeRecipeMain(recipeDeeplink, port) + decodeRecipeMain(recipeDeeplink) .then((decodedRecipe) => { if (decodedRecipe) { console.log('[Main] Recipe decoded successfully, updating window config'); From affb0978e4e3601af7b7fecf78c51302bcb15327 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:16:03 -0700 Subject: [PATCH 4/6] fix markdown links overflowing content and hide agent link previews (#4585) --- .../src/components/MarkdownContent.test.tsx | 68 +++++++++++++++++++ ui/desktop/src/components/MarkdownContent.tsx | 19 +++--- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/ui/desktop/src/components/MarkdownContent.test.tsx b/ui/desktop/src/components/MarkdownContent.test.tsx index 889fb624968e..b3aa6b260ce8 100644 --- a/ui/desktop/src/components/MarkdownContent.test.tsx +++ b/ui/desktop/src/components/MarkdownContent.test.tsx @@ -322,4 +322,72 @@ more text`; }); }); }); + + describe('URL Overflow Handling', () => { + it('handles very long URLs without overflow', async () => { + const longUrl = + 'https://example-docs.com/document/d/1oruk3lcrnhoOXMFzBJB8X6qQ5AtQTmj4XXxXk3xK-3g/edit?usp=sharing&mode=edit&version=1'; + const content = `Check out this document: ${longUrl} + +Another very long URL: https://www.example.com/very/long/path/with/many/segments/and/parameters?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=value5`; + + const { container } = render(); + + await waitFor(() => { + expect(screen.getByText(/Check out this document/)).toBeInTheDocument(); + expect(screen.getByText(/Another very long URL/)).toBeInTheDocument(); + }); + + // Check that URLs are rendered as links + const links = container.querySelectorAll('a'); + expect(links.length).toBeGreaterThan(0); + + // Check that links have proper CSS classes for word breaking + links.forEach((link) => { + // The CSS should allow the text to break + expect(link).toBeInTheDocument(); + }); + }); + + it('handles markdown links with long URLs', async () => { + const longUrl = + 'https://example-docs.com/document/d/1oruk3lcrnhoOXMFzBJB8X6qQ5AtQTmj4XXxXk3xK-3g/edit?usp=sharing&mode=edit&version=1'; + const content = `[Click here for the document](${longUrl})`; + + render(); + + await waitFor(() => { + const link = screen.getByRole('link', { name: 'Click here for the document' }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('href', longUrl); + }); + }); + + it('handles multiple long URLs in the same message', async () => { + const content = `Here are some long URLs: + +1. Example Doc: https://example-docs.com/document/d/1oruk3lcrnhoOXMFzBJB8X6qQ5AtQTmj4XXxXk3xK-3g/edit?usp=sharing&mode=edit&version=1 +2. Another long URL: https://www.example.com/very/long/path/with/many/segments/and/parameters?param1=value1¶m2=value2¶m3=value3 +3. Third URL: https://api.example.com/v1/users/12345/documents/67890/attachments/abcdef123456789?format=json&include=metadata&sort=created_at`; + + render(); + + await waitFor(() => { + expect(screen.getByText(/Here are some long URLs/)).toBeInTheDocument(); + expect(screen.getByText(/Example Doc/)).toBeInTheDocument(); + expect(screen.getByText(/Another long URL/)).toBeInTheDocument(); + expect(screen.getByText(/Third URL/)).toBeInTheDocument(); + }); + }); + + it('applies word-break CSS classes to the container', () => { + const content = 'Test content'; + render(); + + const markdownContainer = document.querySelector('.prose'); + expect(markdownContainer).toBeInTheDocument(); + expect(markdownContainer).toHaveClass('prose-a:break-all'); + expect(markdownContainer).toHaveClass('prose-a:overflow-wrap-anywhere'); + }); + }); }); diff --git a/ui/desktop/src/components/MarkdownContent.tsx b/ui/desktop/src/components/MarkdownContent.tsx index 5c4020fbda31..a8bb679b3516 100644 --- a/ui/desktop/src/components/MarkdownContent.tsx +++ b/ui/desktop/src/components/MarkdownContent.tsx @@ -139,21 +139,22 @@ const MarkdownContent = memo(function MarkdownContent({ return (
Date: Thu, 11 Sep 2025 18:17:21 +1000 Subject: [PATCH 5/6] fix: display recipes without metadata in the recipe list in ui (#4608) --- crates/goose-server/src/routes/recipe_utils.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/goose-server/src/routes/recipe_utils.rs b/crates/goose-server/src/routes/recipe_utils.rs index 8fba2fea6853..f55b19314638 100644 --- a/crates/goose-server/src/routes/recipe_utils.rs +++ b/crates/goose-server/src/routes/recipe_utils.rs @@ -31,7 +31,7 @@ fn short_id_from_path(path: &str) -> String { format!("{:016x}", h) } -fn load_recipes_from_path(path: &PathBuf) -> Result> { +fn load_recipes_from_path(path: &PathBuf, is_global: bool) -> Result> { let mut recipe_manifests_with_path = Vec::new(); if path.exists() { for entry in fs::read_dir(path)? { @@ -43,14 +43,18 @@ fn load_recipes_from_path(path: &PathBuf) -> Result> let Ok(recipe) = Recipe::from_content(&recipe_file.content) else { continue; }; - let Ok(recipe_metadata) = RecipeManifestMetadata::from_yaml_file(&path) else { - continue; - }; let Ok(last_modified) = fs::metadata(path.clone()).map(|m| { chrono::DateTime::::from(m.modified().unwrap()).to_rfc3339() }) else { continue; }; + let recipe_metadata = + RecipeManifestMetadata::from_yaml_file(&path).unwrap_or_else(|_| { + RecipeManifestMetadata { + name: recipe.title.clone(), + is_global, + } + }); let manifest_with_path = RecipeManifestWithPath { id: short_id_from_path(recipe_file.file_path.to_string_lossy().as_ref()), @@ -78,8 +82,8 @@ pub fn get_all_recipes_manifests() -> Result> { let mut recipe_manifests_with_path = Vec::new(); - recipe_manifests_with_path.extend(load_recipes_from_path(&local_recipe_path)?); - recipe_manifests_with_path.extend(load_recipes_from_path(&global_recipe_path)?); + recipe_manifests_with_path.extend(load_recipes_from_path(&local_recipe_path, false)?); + recipe_manifests_with_path.extend(load_recipes_from_path(&global_recipe_path, true)?); recipe_manifests_with_path.sort_by(|a, b| b.last_modified.cmp(&a.last_modified)); Ok(recipe_manifests_with_path) From 881a71a27ec2011ee5c3cda0f81e869ef5b2b6c0 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Thu, 11 Sep 2025 09:13:49 -0400 Subject: [PATCH 6/6] Run the conversation fixer over messages for recipe create (#4605) --- crates/goose/src/agents/agent.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index 5e3eee242d8e..1dad5f494ad1 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -1467,6 +1467,14 @@ impl Agent { tracing::debug!("Retrieved {} tools for recipe creation", tools.len()); messages.push(Message::user().with_text(recipe_prompt)); + + let (messages, issues) = fix_conversation(messages); + if !issues.is_empty() { + issues + .iter() + .for_each(|issue| tracing::warn!(recipe.conversation.issue = issue)); + } + tracing::debug!( "Added recipe prompt to messages, total messages: {}", messages.len()