Conversation
…ture (#2) * feat: scaffold Tauri + React app with window dragging and app icon Sets up the goose2 desktop app with: - Tauri 2 + React 19 + Vite + Tailwind CSS - Custom titlebar with drag-to-move and double-click maximize/restore - Window state persistence via tauri-plugin-window-state - App icon ported from goose-desktop (cyan/amber yin-yang design) - Theme system with dark/light/system modes - Hermit toolchain (node, pnpm, biome, just, lefthook) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add app shell layout with tabs, sidebar, and status bar Adds the main application layout ported from goose2.0's visual design: - TabBar with session tabs, home button, and sidebar toggle - Collapsible sidebar with smooth width transition - Status bar with model name, token count, and status indicator - AppShell component wiring everything together Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add settings modal with theme section + quality fixes Settings: - Settings modal with sidebar nav (Appearance, General, About) - Theme selector (Light/Dark/System) wired to ThemeProvider - Accent color picker and density selector (visual placeholders) - Open via Cmd+, or Settings button in sidebar Quality fixes: - Fix stale state bug in tab close handler - Fix unreachable useTheme guard, add OS theme change listener - Improve accessibility (ARIA roles, labels, semantic elements) - Add prefers-reduced-motion support - Extract shared Tab type to features/tabs/types.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove references to nonexistent theme-tokens module The previous commit introduced imports for a theme-tokens module that doesn't exist. Remove those imports and define the needed types locally in ThemeProvider. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: wire up accent colors and density, add AGENTS.md Theming: - ThemeProvider now manages accent color and density (persisted to localStorage) - Accent color applied via --color-accent CSS variable on :root - Density applied via --density-spacing CSS variable (multiplier) - AppearanceSettings uses ThemeProvider context instead of local state - Tailwind config maps accent color variable - Settings modal animations enhanced to match goose2.0 reference - Component classes updated to use semantic token names Documentation: - AGENTS.md captures all project conventions, patterns, and decisions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace ambiguous Tailwind arbitrary durations with config values Add duration-400 and duration-600 to Tailwind config to avoid ambiguous class warnings from arbitrary value syntax. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: sidebar minimizes to icon strip instead of hiding The sidebar now shrinks to a 48px icon-only column when toggled, instead of disappearing completely. Icons stay visible with tooltips, labels and section headers are hidden. Smooth width transition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: improve settings button alignment and styling in sidebar Match goose2.0's footer pattern with proper padding, full-opacity border, flex alignment, and smooth transition between states. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add home screen with clock, greeting, and hidden status bar Home screen shows a live digital clock, time-based greeting, and placeholder chat input. Status bar hides when on the home view (no active tab), matching goose2.0's clean home experience. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: improve home screen to match goose2.0, add chat view Home screen: large mono clock with AM/PM, time-based greeting, goose2.0-style chat input (rounded-2xl, shadow-lg, model badge). Chat view: input at bottom with scrollable messages area. Home vs chat differentiated by active tab state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: chat view now fills the full available height Remove flex from main element so child components can properly resolve h-full against the block parent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add testing infrastructure (Vitest, Playwright, CI) Testing: - Vitest for unit/component tests with jsdom + React Testing Library - 13 tests across 3 test files (ThemeProvider, cn utility, HomeScreen) - Playwright for E2E smoke tests (4 tests) - Test setup with matchMedia mock for jsdom Tooling: - Just targets: test, test-watch, test-coverage, test-e2e - Lefthook pre-push now runs unit tests - CI gate includes tests CI/CD: - GitHub Actions workflow with 4 jobs: lint, test, desktop, rust-lint - Playwright artifacts uploaded on failure - Rust caching for faster builds Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve all Biome formatting and lint issues Auto-format all files with Biome. Fix lint issues: - Add type="button" to all interactive buttons - Replace span[role=button] with proper button element - Add dialog role to settings modal - Biome-ignore for standard accessibility !important pattern All checks pass: pnpm check, pnpm test, pnpm build. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: add src-tauri/target/ to .gitignore Prevent Rust/Tauri build artifacts from being committed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: exclude Tauri generated schemas from Biome formatting * chore: update .gitignore for full project coverage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pin CI actions to commit SHAs for Semgrep compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve CI failures and improve lefthook auto-formatting - Fix dtolnay/rust-toolchain action SHA (was invalid, causing all Rust CI jobs to fail) - Fix Biome folder ignore pattern (remove trailing /**) - Update lefthook pre-commit to auto-format with biome instead of just checking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use vite preview directly with explicit host for E2E server The Playwright webServer was using `pnpm preview --port 4173` which invoked vite preview without --host, causing it to bind to localhost (IPv6 ::1) instead of 127.0.0.1. This caused ERR_CONNECTION_REFUSED in CI and locally. Switch to npx vite preview with --host 127.0.0.1 and --strictPort to ensure reliable binding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: gracefully handle non-Tauri browser environment Check for Tauri runtime before calling window APIs so the app renders correctly in browser-based E2E tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: make E2E smoke tests more robust with longer timeouts - Add 10s timeout for element visibility (CI can be slow to render) - Use .first() for model badge to handle multiple matches - Fix strict mode violation for duplicate text matches Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: smooth transition for status bar show/hide Status bar now slides and fades in/out with a 300ms transition instead of appearing/disappearing abruptly when switching views. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use dynamic import for Tauri API to prevent browser crash Static import of @tauri-apps/api/window fails at module load time in non-Tauri environments. Use dynamic import() guarded by __TAURI_INTERNALS__ check so the app renders in Playwright. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use python http.server for E2E tests, start with empty tabs Switch Playwright webServer from npx vite preview to python3 http.server for more reliable static file serving in CI. Start app with no tabs open (home screen by default). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align E2E setup with Sprout's proven configuration - Use url instead of port for webServer readiness check - Remove -b 127.0.0.1 flag from python http.server command - Add explicit cwd to webServer config - Use pnpm exec instead of npx for Playwright in CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove unused @tauri-apps/plugin-opener JS dependency The Rust-side tauri-plugin-opener is still registered, but the JS binding package was never imported from frontend code. Removing it keeps the dependency list honest. Dark mode colors already match goose2.0 — no changes needed there. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: port floating sidebar from goose2.0 - Rewrite Sidebar.tsx with floating panel design (backdrop blur, rounded corners, shadow) - Smooth collapse/expand animation (48px ↔ 240px) with staggered label transitions - Search bar morphs between icon-only and full bar with ⌘K badge - Nav items with staggered fade-in animation delays - Collapsible recent chats section - User avatar footer as settings trigger - GooseIcon SVG in header (replaces emoji) - Update AppShell layout: sidebar wrapped in padding div for floating effect - Add ⌘B keyboard shortcut for sidebar toggle - All color tokens mapped to goose2 design system * fix: add missing GooseIcon and GooseLogo files * fix: remove duplicate sidebar toggle, fix traffic lights on blur - Remove PanelLeft sidebar toggle from TabBar (sidebar has its own toggle) - Add solid background behind macOS traffic light area so window controls remain visible when the window loses focus - Clean up unused onSidebarToggle prop from TabBar/AppShell * fix: make TabBar fully opaque so macOS traffic lights stay visible on blur The previous fix added a solid background div behind the traffic light area, but it rendered within the semi-transparent parent context (bg-background/80 backdrop-blur-sm). The OS compositor still saw transparency and hid the traffic lights on window blur. Simply making the TabBar background fully opaque fixes this properly. * fix: clean up nav items, subtle ⌘K badge, remove backdrop-blur for traffic lights - Nav items: keep only New Chat, Agents, Skills (removed Tasks, Artifacts, Apps) - ⌘K badge: lighter text color (foreground-secondary/40), no background - Sidebar: remove backdrop-blur-xl and make bg fully opaque — fixes macOS traffic lights disappearing on window blur * fix: improve sidebar border contrast in dark mode In dark mode, border-primary (#3f434b) and background-secondary (#3f434b) are identical, making sidebar dividers and the search border invisible. Switched all internal sidebar borders/dividers to border-secondary (#525b68) which provides clear contrast against the sidebar background in both themes. * fix: align dark mode palette with goose2.0 Updated dark mode color tokens to match goose2.0's much darker palette: - background-primary: #22252a → #0d0d0d (near black, was too light) - background-secondary: #3f434b → #141414 (sidebar, card-like) - background-tertiary: #474e57 → #262626 (muted surfaces) - Borders, text, and ring colors adjusted accordingly - This also fixes the dark mode border visibility issue since border-primary (#262626) is now distinct from background-secondary (#141414) * fix: revert custom traffic light code, rely on Tauri's built-in trafficLightPosition The custom Rust code that called setHidden:NO on focus events was interfering with Tauri's native traffic light positioning, causing them to shift down. Removed the custom code entirely — Tauri's trafficLightPosition config in tauri.conf.json handles positioning correctly on its own.
- Add SkillsView and AgentsView stub pages matching goose2.0 layout (title left, action button right, search bar, empty state) - Add MainPanelLayout shared component for page layout - Add SearchBar shared component for page-level search - Add page-transition CSS animation (matching goose2.0) - Add storage path constants (~/.goose/) - Wire up sidebar nav items (Agents, Skills) with view routing - Add AppView type and view state management to AppShell - Sidebar active state uses subtle bg-background-secondary treatment
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
….0 visual match (#7) * feat: chat experience preparation — UI components, stores, and goose2.0-style chat input Add chat and agent management UI infrastructure without ACP protocol implementation: - Rich chat UI: ChatInput (matching goose2.0 design with mode/model/folder pickers), MessageBubble, MessageTimeline, StreamingIndicator, ThinkingBlock, ToolCallCard - Agent/Persona management: PersonaCard, PersonaEditor, PersonaGallery, AgentConfig - Zustand stores for chat state, agent state, session management - Rust backend: persona CRUD, session storage, sidecar management commands - Shared UI primitives: DropdownMenu, Tooltip, Popover (Radix UI) - Type system for messages (text, images, tool calls, thinking, reasoning) - Fix Agents page spacing to match Skills page layout Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: match goose2.0 visual design — theme colors, message styling, and UI polish - Update theme colors to pure monochrome (remove blue-gray tint) matching goose2.0 - Restyle MessageBubble: remove bubble backgrounds, right-align user messages, smaller avatars, dimmed agent text, hover-reveal actions - Restyle ToolCallCard as compact inline pills with status colors and elapsed time - Restyle ThinkingBlock with brain icon circle and left-border content indent - Fix tab close button right padding (px-3 → pl-3 pr-1.5) - Replace hardcoded "WB" initials with generic User icon in sidebar Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve Rust fmt and clippy dead_code warnings for CI Allow dead_code on preparation types/methods that will be wired up when ACP integration lands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use getByPlaceholderText in E2E test for chat input The placeholder text is an HTML attribute, not visible text content, so getByText doesn't find it. Switch to getByPlaceholderText. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use CSS selector for placeholder text in E2E smoke test getByPlaceholderText is not available in this Playwright version. Use locator with attribute selector instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
… fixes (#8) * feat: personas, skills, and import/export improvements - Rename "Agents" to "Personas" across the UI (sidebar, headings, descriptions) - Load markdown persona files from ~/.goose/agents/ on startup with periodic refresh (60s interval + window focus), parsed from YAML frontmatter + body - Implement persona import/export matching sprout's .persona.json v1 format with export from card menu and import via file picker - Implement "New Skill" creation dialog with kebab-case validation, writing SKILL.md files to ~/.goose/skills/{name}/ - Add backend path traversal protection for skill name validation - Fix persona creation to persist through backend API (was local-only) - Fix import to refresh from backend instead of creating store duplicates - Disable delete button for disk-loaded personas in the UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add edit, duplicate, export, and import for skills - Add update_skill backend command to edit existing SKILL.md files - Add export_skill command returning sprout-compatible .skill.json format - Add import_skills command with file validation and name collision handling - Convert CreateSkillDialog to support edit mode (read-only name, pre-filled fields) - Add dropdown menu on skill cards with Edit, Duplicate, Export, Delete - Add Import button with file picker on Skills page - Duplicate generates unique {name}-copy suffix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: make skills and personas pages fill full height with proper scrolling Both pages now use flex column layout with h-full to fill the parent container, and an inner scrollable wrapper with overflow-y-auto for content overflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix cargo fmt and clippy warnings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: delete persistence, confirmation dialogs, and export notifications - Fix persona deletion to persist via backend API (was local-only) - Add delete confirmation dialog for both personas and skills - Show export notification toast with filename after successful export Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: hide delete option for built-in and disk-loaded personas Instead of showing a disabled delete button, hide it entirely for personas that can't be deleted (built-in and markdown-loaded). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: update PersonaCard test for hidden delete on built-in personas The delete menu item is now hidden (not disabled) for built-in and disk-loaded personas, so the test checks it's absent rather than disabled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add comprehensive tests for skills and persona features - CreateSkillDialog: 17 tests (rendering, name validation, edit mode, form submission) - SkillsView: 11 tests (rendering, search, card menu, delete confirmation) - usePersonas hook: 6 tests (loading, CRUD, refresh) - Agents API: 3 tests (export, import, refresh invoke calls) Total test count: 100 → 137 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: persona & skill dialog layouts + drag-and-drop on new cards Restructure PersonaEditor and CreateSkillDialog to use fixed header/footer with a scrollable middle area so action buttons stay visible. Add useFileImportZone hook and wire drag-and-drop file import onto the "New Persona" and "New Skill" cards. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: update SkillsView empty state text assertion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Replace default Tauri icons with Goose app icon Copied the goose silhouette icon from the goose desktop app into all Tauri icon slots (macOS, Windows, iOS, Android) so the app displays the correct brand icon. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add proper padding to app icon for correct dock sizing The icon was filling the entire canvas edge-to-edge, making it appear ~25% larger than other apps in the dock and Alt+Tab bar. Added ~10% transparent padding on each side following Apple's icon guidelines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add ACP client integration for multi-agent support Port the acp-client crate from builderbot and integrate it into the goose2 Tauri app. This enables connecting to ACP-compatible agents (Goose, Claude Code, Codex, Pi, Amp) over stdio. Rust backend: - acp-client crate: AcpDriver, MessageWriter/Store traits, agent discovery, session management with replay, !Send workaround - services/acp.rs: TauriMessageWriter (streams via events), TauriStore (session persistence), AcpService - commands/acp.rs: discover_acp_providers, acp_send_message Frontend: - api/acp.ts: provider discovery + message sending - useAcpStream.ts: Tauri event listener for streaming responses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: wire ACP chat flow with default Goose agent - Create default Goose ACP agent on first launch (auto-selected) - Replace mock chat with real ACP routing in useChat - Enable useAcpStream in ChatView for streaming responses - Add 13 tests for ACP streaming + default agent creation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: nested button hydration error and streaming text accumulation - TabBar: restructure tab as div[role=tab] with span close target to eliminate button-inside-button HTML violation - useAcpStream: accumulate text chunks in a ref before passing to updateStreamingText, so the full text is displayed instead of only the last chunk - useChat: remove unused biome suppression comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve three critical ACP messaging bugs 1. Parameter name mismatch: use snake_case keys in Tauri invoke to match Rust function parameter names 2. Premature cleanup: don't clear streamingMessageId in finally block — acpSendMessage returns immediately (Rust runs in bg), let useAcpStream handle cleanup on acp:done event 3. Timing race: enable useAcpStream when streamingMessageId is set (not chatState), so listener is active before events arrive Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve three ACP streaming bugs - Use camelCase params in acp_send_message invoke (Tauri 2 default) - Always enable useAcpStream listener to avoid missing events - Clear streamingMessageId on non-abort errors to unstick UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract duplicate findLastIndex to shared utility Both chatStore.ts and useChat.ts had identical findLastIndex helper functions. Extracted to src/shared/lib/arrays.ts following the existing shared/lib pattern (cn.ts). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove binaryPath from AcpProvider, rewrite hook tests - Remove binaryPath from AcpProvider interface (backend only sends id/label) - Rewrite useAcpStream tests to mock Tauri listen() and use renderHook() - Tests now verify actual hook behavior: event dispatch, session filtering, text accumulation, cleanup on unmount, enabled flag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add markdown rendering and fix UI spacing to match goose-spence Add MarkdownContent component with syntax highlighting, GFM support, and copy-to-clipboard on code blocks. Update MessageBubble, ThinkingBlock, and MessageTimeline to use markdown rendering and proper spacing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: correct inline code detection for react-markdown v9 and add dark mode prose support Remove deprecated `inline` prop and detect block code via language class or newline content. Add `dark:prose-invert` so prose text is readable in dark mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add AcpSessionRegistry for robust process lifecycle management Prevents dangling `goose acp` processes by tracking running ACP sessions with CancellationTokens, cancelling all on app exit, and cleaning up stale session files on startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix cargo fmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The content div overlapped the absolutely-positioned close button, intercepting click events. Adding z-10 ensures the button stays above the content layer. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When overflow-y is set to auto, browsers implicitly set overflow-x to auto as well, creating an unwanted horizontal scrollbar. Explicitly set overflow-x-hidden on the sidebar nav element. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add projects backend and frontend foundation
- Rust backend: CRUD for projects stored at ~/.goose/projects/{slug}/project.json
- Frontend: API layer, Zustand store, CreateProjectDialog, ProjectsView
- Project model: name, description, prompt, icon, color, preferredProvider/Model, workingDir, useWorktrees
- Tab type extended with optional projectId
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: redesign sidebar with projects + chats hierarchy
Replace the "Recent" section with a goose-spence-style layout:
- Projects section with collapsible groups and nested chats
- Standalone chats section for sessions not in any project
- Remove "Projects" from top nav items (now in sidebar body)
- Extract SidebarProjectsSection to stay under file size limit
- Wire AppShell to pass project data and callbacks to Sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add [+] button to PROJECTS header in sidebar
- PROJECTS section always visible (even with no projects)
- [+] button on PROJECTS header opens CreateProjectDialog
- Wire onCreateProject through Sidebar → AppShell
- CreateProjectDialog rendered in AppShell, refreshes project list on create
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: simplify project dialog and use color dots
- Remove Description field (Prompt serves as description)
- Hide Icon field (always uses default)
- Remove Preferred Model field
- Replace Provider text input with ACP provider dropdown
- Add Browse button for Working Directory (with Tauri dialog fallback)
- Show color dots instead of emoji icons in sidebar and project cards
- Show prompt preview in project cards instead of description
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: always-visible create project button and 3-dot menus
- Make [+] create project button always visible (not hover-only)
- Match button size/alignment with "New chat" buttons
- Add 3-dot menus (Edit/Archive) to project headers
- Add 3-dot menus (Edit/Archive) to chat items (nested + standalone)
- Menus appear on hover, close on outside click
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: fix alignment, edit project dialog, browse button, menu behavior
- Align [+] create project button with project row [+] buttons (px-1.5)
- Wire "Edit" on project menu to open CreateProjectDialog with project data
- Chat menus now only show "Archive" (removed Edit)
- Install tauri-plugin-dialog for native folder picker (Browse button)
- Add dialog:allow-open to Tauri capabilities
- Fix 3-dot menus staying visible: force opacity when dropdown is open
- Remove unused onEditChat prop
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: always-visible 3-dot menus and tighter header alignment
- Make 3-dot menus always visible at 60% opacity (not hover-dependent)
- Remove mr-1 from PROJECTS header [+] button to reduce right gap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: proper hover behavior for 3-dot menus and header alignment
- Restore hover show/hide for 3-dot menus with open-state override
- When dropdown is open, force trigger button visible (opacity-100)
- When closed, standard group-hover behavior applies
- Fix header [+] alignment: remove container px, use pl on text + mr-1 on button
- Both PROJECTS header [+] and project row [+] now have identical right offset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: auto-expand project when creating a new chat in it
When the active tab changes to one inside a project, automatically
expand that project in the sidebar so the new chat is visible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve pre-push lint issues
- cargo fmt: fix Rust formatting in projects.rs
- clippy: allow too_many_arguments on create_project and update_project
- biome: replace non-null assertions with local variable in Sidebar.tsx
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: wire archive actions and add project ordering
- Archive project: deletes from filesystem and refreshes sidebar
- Archive chat: closes the tab and cleans up session
- Add `order` field to ProjectInfo (auto-assigned on create, preserved on update)
- Projects sorted by order instead of alphabetically
- Backwards-compatible: existing projects without order default to 0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: consistent hover show/hide for [+] and 3-dot buttons
Use invisible/visible + opacity combo for both ItemMenu trigger and
[+] new chat button. The invisible class provides reliable show/hide
while opacity handles the visual transition. Both now appear and
disappear in sync on group hover.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: soft-delete archive for projects with restore in settings
- Add archivedAt field to ProjectInfo (null = active, timestamp = archived)
- list_projects filters out archived; new list_archived_projects returns only archived
- archive_project sets archivedAt timestamp; restore_project clears it
- Archive action in sidebar now soft-deletes instead of hard-deleting
- Settings modal shows "Archived Projects" section with Restore and Delete buttons
- Delete permanently option available for true removal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: move archived projects to dedicated Projects tab in settings
Add a "Projects" tab to the settings modal with the FolderKanban icon.
Archived projects section moved from General to its own tab for
cleaner organization.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add confirmation dialog for permanent project deletion
Clicking "Delete" on an archived project in Settings now shows a
confirmation dialog before permanently removing the project.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The root /src/stores/ and /src/types/ directories contained dead code that was superseded by feature-level stores and /src/shared/types/. None of these files were imported anywhere in the active codebase. Removed: - src/stores/ (agentConfigStore, chatStore, sessionStore, skillStore) - src/types/ (chat.ts, index.ts) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: repair broken settings modal layout The <nav> element with settings navigation buttons was a sibling of the sidebar div instead of being nested inside it, and the main flex container was missing its closing tag. This broke the sidebar/content flex layout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore missing padding wrapper around Settings title The merge in #15 dropped a wrapper div with px-4 py-4 padding and entrance animation around the "Settings" heading, causing it to be smashed against the top-left corner of the sidebar. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: consolidate types and stores into feature-sliced architecture Removed root-level src/types/ and src/stores/ directories that contained outdated duplicates of types and unused store implementations. Moved unique config types (AgentConfig, Skill, Provider, Model) to src/shared/types/config.ts. Relocated stores to their owning features (agentConfigStore → agents, sessionStore → chat, skillStore → skills). Deduplicated ChatState, TokenState, and Session types — src/shared/types/chat.ts is now canonical. Updated AGENTS.md with clear guidelines on where types and stores should live. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: replace mode selector with ACP provider selector in chat input Replace the placeholder mode selector (agent/ask/plan) with a functional provider selector that discovers available ACP providers at runtime and routes messages through the selected provider. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: derive chat agent name from selected provider Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update HomeScreen tests for provider selector Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use flexible placeholder matcher in e2e smoke test The Home page placeholder now reflects the selected ACP provider name instead of hardcoded "Goose". Use a starts-with selector so the test passes regardless of which provider label is shown. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…iguation (#19) * feat: add branch-based dev icon for worktree disambiguation Add a Swift script that generates a modified .icns file with a blue badge containing the current branch name. When running in a git worktree, the `just dev` and `just dev-debug` recipes automatically detect this and generate a badged icon, making it easy to distinguish between multiple dev instances in the dock. - scripts/generate-dev-icon.swift: Generates badged icons with text wrapping on hyphens, multi-size iconset generation via iconutil - justfile: dev and dev-debug recipes now detect worktrees and pass icon override config to Tauri via --config JSON merge - Generated icons go to src-tauri/target/dev-icons/ (already gitignored) * feat: derive stable dev port from working directory for worktree disambiguation Each worktree gets a stable, unique port derived from the SHA-256 hash of the working directory path (range 10000-64999). This prevents port conflicts when running multiple dev instances simultaneously. - justfile: dev and dev-debug recipes compute VITE_PORT and pass it via --config JSON merge to override devUrl and beforeDevCommand - vite.config.ts: server port and HMR port read from VITE_PORT env var, falling back to 1520 for standalone frontend dev (just dev-frontend) - Icon config in dev recipe now merges into existing TAURI_CONFIG via python3 JSON manipulation instead of replacing it - Icon config in dev-debug recipe appends a separate --config flag
* feat: use acp-client from builderbot repo as git dependency Remove the local acp-client crate (crates/acp-client/) and replace it with a git dependency pointing to block/builderbot on the main branch. The builderbot version is a strict superset of the local copy — same public API (AcpDriver, AgentDriver, MessageWriter, Store, discover_providers) plus remote workspace support, concurrent provider discovery, structured logging, and graceful shutdown. Changes: - Remove 'crates/acp-client' from workspace.members in Cargo.toml - Replace path dependency with git dependency (block/builderbot, main) - Delete src-tauri/crates/acp-client/ directory (5 files, ~1640 lines) - Verified cargo check passes with no code changes needed in goose2 * fix: pin acp-client to rev and clean up config - Pin acp-client git dependency to rev 5af819e instead of branch=main for reproducible builds - Remove redundant single-member [workspace] table from Cargo.toml - Remove deprecated baseUrl from tsconfig.json (TS 5.9+ deprecation) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update acp-client pin to latest merge commit Pin acp-client to rev 17e6098 (builderbot PR #554 merge commit) to pick up the latest acp crate changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add doctor health-check backend commands
* feat: add doctor health-check UI to preferences dialog
Port the doctor feature frontend from staged into React components that
match goose2's patterns (Tailwind CSS, lucide-react, cn() utility).
New files:
- src/shared/api/doctor.ts: Tauri API bindings for run_doctor and
run_doctor_fix commands
- src/features/settings/ui/DoctorCheckRow.tsx: Individual check row
component with status icons, fix command dialog, and external link
support
- src/features/settings/ui/DoctorSettings.tsx: Doctor panel with
auto-run on mount, re-run button, copy debug report, and
tool/agent section grouping
Modified:
- src/features/settings/ui/SettingsModal.tsx: Add Doctor tab with
Stethoscope icon between Appearance and General nav items
* fix: add right padding to doctor header to avoid overlap with close button
The Re-run and Copy details buttons in the Doctor settings panel were
rendered behind the modal's X close button due to insufficient clearance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align doctor panel heading and nav position with other settings panels
Remove the inline Stethoscope icon from the Doctor heading to match
the plain h3 style used by all other settings panels. Move the Doctor
nav item to sit between Projects and About in the sidebar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: refresh ACP providers after doctor checks and fixes
After running doctor checks or applying a fix, call discoverAcpProviders()
so newly-installed agents appear in the agent selector without requiring
an app reload. This matches the Staged implementation where
DoctorSettingsPanel.svelte calls refreshProviders() after runChecks().
Add a runChecksAndRefresh wrapper that awaits runChecks() then calls
discoverAcpProviders(), guarded by the existing mountedRef check. Use it
in all three call sites: useEffect on mount, Re-run button onClick, and
the onFixed callback passed to DoctorCheckRow.
* fix: use Tauri opener plugin instead of window.open for external URLs
Replace window.open(url, '_blank') with openUrl() from
@tauri-apps/plugin-opener in DoctorCheckRow. In a Tauri webview,
window.open is unreliable for opening URLs in the system browser.
The opener plugin uses the native OS handler and is already configured
in the Tauri backend (opener:default capability).
- Add @tauri-apps/plugin-opener as a frontend dependency
- Import openUrl from @tauri-apps/plugin-opener
- Replace window.open(check.fixUrl, '_blank') with void openUrl(check.fixUrl)
* fix: use ID-based fix execution instead of passing shell commands over IPC
The frontend previously sent raw shell command strings to the backend's
run_doctor_fix handler, which would execute them directly. This is a
security concern as the IPC boundary should not accept arbitrary commands.
Now the backend is the single source of truth for fix commands. The
frontend sends only a check ID and fix type ("command" or "bridge"),
and the backend looks up the corresponding command from the static
AgentCheckInfo definitions or hardcoded tool check commands. Unknown
IDs or fix types are rejected with an error.
- Add fix_type field to DoctorCheck struct (set alongside fix_command)
- Add lookup_fix_command() that maps check_id + fix_type to a command
- Rewrite run_doctor_fix to accept (check_id, fix_type) instead of a
raw command string
- Update frontend API and DoctorCheckRow to pass check.id + check.fixType
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve doctor code review comments
- Replace stringly-typed fix_type (Option<String>) with a FixType enum
on both backend and frontend, making invalid states unrepresentable
and eliminating the wildcard fallback in lookup_fix_command
- Collapse duplicate "not installed" fallback branches in
check_agent_install into a single branch that conditionally appends
the main-binary search output
- Align fix confirmation dialog with existing dialog patterns: add
role="dialog", aria-modal, aria-label, Escape key handler, and match
the class ordering used by other confirmation dialogs in the codebase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: individual chat sessions with persistence and tab management Add full chat session lifecycle — create, switch, persist, and restore individual chat sessions backed by ACP. Key capabilities: - Session persistence: messages saved to per-session directories, metadata consolidated in ~/.goose/sessions/metadata.json - Tab state management: Zustand-based chatSessionStore with tab open/close/reorder/activate, persisted to ~/.goose/ui_state.json - Session restoration: historical messages loaded lazily from backend on tab select; tab state restored and validated on app launch - Sidebar recents: all sessions shown sorted by updatedAt, with visual distinction between open and background sessions - ACP integration: multi-agent support via acp-client, provider selector in chat and home views - UI foundations: skills/personas pages, projects sidebar, settings fixes, dev icons, chat components, and visual polish Also includes earlier PR work: sidebar navigation, ACP client, Tauri icon, dependency updates, and various bug fixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix Rust formatting in session and UI state commands Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve code review issues in session persistence and UI - Fix race condition: acquire mutex in get_messages to prevent reading partially-written message files during concurrent add_message calls - Log errors in save_metadata/save_messages instead of silently discarding them, aiding diagnosis of disk/serialization failures - Refactor AppShell mount effect from nested .then() to async IIFE - Use Map for O(1) session lookups in tab derivation - Only remove session from local store on successful backend delete in archiveSession to avoid stale entries on failure - Add proper exhaustive deps to ChatView initial message effect, guarded by ref to maintain mount-only semantics Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update acp-client git dependency from 17e60981 to dbd5bc9c. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add persona picker and @ mention support in chat Replace the provider-first model with a persona-first interaction pattern. Users now pick a Persona (with its own system prompt, default provider, and model) from a dropdown in both the home screen and chat input. Typing @ in the chat textarea opens an autocomplete to mention any persona inline, switching context for that message. Provider selection is still available as a secondary override. New components: - PersonaPicker: dropdown grouped by built-in / custom personas - MentionAutocomplete: floating autocomplete triggered by @ in textarea - ChatInputToolbar: extracted bottom bar to keep ChatInput under 500 lines - useMentionDetection: hook for detecting @ mention state in text input Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: wire persona system prompts through to ACP backend and fix mention UX - Pass persona system_prompt from frontend through Tauri to AcpService - Prepend persona instructions to user prompt in <persona-instructions> tags - Add keyboard navigation (arrow keys, Enter/Tab) to @ mention autocomplete - Fix PersonaPicker description extraction (was using index [1] instead of [0]) - Simplify ChatView handleSend by letting useChat handle system prompt injection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: enhance persona picker with @ mentions on home screen, switch notifications, and per-message persona tracking Adds @ mention autocomplete to the HomeScreen input so users can invoke personas before starting a chat. Shows system notifications when switching personas mid-chat, and tracks which persona generated each assistant message in the timeline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: persist persona selection across tab switches and app restarts Store the selected persona per session in ui_state.json alongside the existing tab state. When switching tabs or reopening the app, the previously selected persona is restored for each chat session. Changes: - Backend: add persona_per_session HashMap to UiState with serde(default) for backward compatibility - chatSessionStore: add personaId to ChatSession, save/restore persona mapping in persistTabState/loadTabState - ChatView: save persona changes to session store - AppShell: pass session's stored personaId to ChatView, store personaId when creating new sessions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve 4 bugs found in persona picker audit 1. Error state stuck: acpSendMessage errors now reset chatState to "idle" so the user can continue sending messages after failures. 2. Stop button cancels backend: stopGeneration now calls acp_cancel_session to terminate the Rust ACP session, preventing orphaned streaming events after the UI resets. 3. @ mention persona race condition: when switching personas via @ mention, the message send is deferred to the next render cycle so useChat picks up the new persona's system prompt instead of the stale one. 4. Deleted persona fallback: if a persisted personaId references a deleted persona, ChatView now falls back to builtin-goose (or the first available persona) and updates the session store. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: load personas eagerly at app startup Personas were only loaded when visiting the Personas page (via the usePersonas hook in AgentsView). This meant the persona picker in HomeScreen and ChatInput showed empty until the user navigated to the Personas page at least once. Now AppShell loads personas from the backend in its startup effect, alongside sessions and tab state. This ensures personas are available immediately in all views. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix cargo fmt in acp service Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update tests for persona picker changes and silence clippy - Update ChatInput tests for new toolbar structure and persona props - Update HomeScreen tests to mock persona store instead of ACP providers - Add #[allow(clippy::too_many_arguments)] on send_prompt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: surface ACP session title, model state, and session ID in UI
Wire up new acp-client MessageWriter callbacks (on_session_info_update,
on_model_state_update) to Tauri events and frontend listeners. Session
titles and model names from ACP providers now update the sidebar, tabs,
and status bar reactively. Fallback title generation from the first user
message when the provider doesn't send SessionInfoUpdate. Add session ID
display with copy button to the status bar. Fix doubled streaming text
caused by React StrictMode double-mounting with async listener cleanup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: persist session metadata to backend across app restarts
Extend the backend Session struct with provider_id, persona_id, and
model_name fields. Add a generic update_session Tauri command so the
frontend can write-through any metadata change to disk. The frontend
chatSessionStore now persists title, provider, persona, and model on
every updateSession call and restores them from the backend on load.
Remove the persona-per-session hack from ui_state — persona now lives
on the session itself. Hide token count in status bar when zero.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update e2e test for removed model badge default
The home screen no longer shows a hardcoded "Claude Sonnet 4" text since
model info now comes from ACP. Update the smoke test to check for the
persona picker ("Goose") which is always visible on the home screen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove stale model badge e2e test
The model badge test matched a hidden SVG <title>Goose</title> element
instead of visible UI. The underlying feature (static model name on home
screen) no longer exists — model info is now dynamic from ACP. The chat
input placeholder is already covered by an existing test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add archive/unarchive session support to frontend types, API, and store Replace the delete-based archiveSession with actual archive/unarchive API calls. Add archivedAt field to Session and ChatSession types, wire up new Tauri invoke bindings, and update the Zustand store to mark sessions as archived instead of removing them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add show more/less toggle to sidebar project and recents sections Show 3 chats by default in both project sections and recents. For ≤8 items, toggle inline expansion. For >8 items, navigate to the full projects view instead. Follows the goose-spence pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Archive chats and restore them from settings * Refresh project list after restore --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use project settings in project chats * Allow clippy on ACP send command
The @tailwindcss/typography plugin injects backtick characters via CSS ::before and ::after pseudo-elements on <code> tags. This caused inline code to display with visible backticks around the highlighted text. Override with content-none on the prose-code pseudo-elements. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ts (#31) Always open the app on the Home page instead of restoring the last active chat. Tabs are still restored in the tab bar so nothing is lost. Project chats created via the "+" button now start with the title "New Chat" instead of the project name, allowing the ACP title logic to generate a proper title from the first message or session info — matching how regular chats work. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bede5a5154
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| SessionUpdate::UserMessageChunk(chunk) => { | ||
| if let AcpContentBlock::Text(text) = &chunk.content { | ||
| // Finalize any in-progress assistant message and count replay event |
There was a problem hiding this comment.
Replay image user chunks instead of dropping them
The replay path only handles UserMessageChunk content when it is text, so non-text user blocks are silently discarded during acp_load_session. Since prompt sending explicitly supports image blocks, reloading a session with image attachments loses those attachments (and image-only prompts degrade to empty/placeholder text), which corrupts visible history for that conversation.
Useful? React with 👍 / 👎.
| if let Some(message_id) = replay_msg_id { | ||
| let _ = self.app_handle.emit( |
There was a problem hiding this comment.
Create replay assistant message before tool events
Replay emits tool_call only when replay_message_id already exists, but that ID is created only in the AgentMessageChunk(Text) branch. If a replayed assistant turn starts with tool calls (or contains only tool events), those tool updates are dropped because no message was created yet, so reloaded history is incomplete. Initialize the replay message on first tool event as well.
Useful? React with 👍 / 👎.
Adapted from the original goose2 repo CI with paths adjusted for the monorepo layout (ui/goose2/). Only triggers on changes to ui/goose2/** to avoid unnecessary runs. Signed-off-by: Jack Amadeo <jackamadeo@squareup.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de6ddad44d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| store.setChatState(sessionId, "streaming"); | ||
| // When images are present with no text, pass a single space so the ACP | ||
| // driver doesn't send an empty text content block that goose rejects. | ||
| const acpPrompt = text.trim() || (images?.length ? " " : text); | ||
| await acpSendMessage(sessionId, providerId, acpPrompt, { |
There was a problem hiding this comment.
Skip send when stop is requested during session prepare
stopGeneration() can run while acpPrepareSession/acpSetModel are still awaiting, but this path never checks whether the local abort was triggered before transitioning to streaming and dispatching the prompt. In that race (especially on first-send draft sessions), the backend can treat the request as canceled while the UI has already entered streaming, and no matching completion event is guaranteed, leaving the session stuck busy. Add an abort/cancel guard immediately after the prepare/model awaits and return early before setChatState("streaming") + acpSendMessage.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd874dc281
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| title: text.trim().slice(0, 100), | ||
| updatedAt: new Date().toISOString(), |
There was a problem hiding this comment.
Preserve default title for image-only first prompts
When the first user turn contains only images, text.trim() is empty, so this path immediately sets the session title to "". Because the title is no longer equal to the default placeholder, the later acp:done fallback that derives a better title is skipped, leaving the chat with a permanently blank title in the sidebar/history. Keep the default title (or use a non-empty fallback) when trimmed text is empty.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff12d2f684
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(route) = routes.get_mut(&goose_session_id) { | ||
| route.replay_message_id = Some(new_id.clone()); | ||
| route.replay_events += 1; | ||
| } |
There was a problem hiding this comment.
Track all replay notifications before marking replay complete
load_session_inner waits for replay completion using get_replay_event_count, but this counter is only incremented when a new replay assistant message is created (route.replay_events += 1) and not for later chunks of that same message. If additional replay text/tool chunks are still queued, the drain check can finish early, finalize_replay emits acp:done, and late chunks are then ignored by the frontend’s completed-message guard, truncating restored history for multi-chunk replies.
Useful? React with 👍 / 👎.
| dispatcher.emit_model_state(local_session_id, None, models); | ||
| } | ||
| if let Some(options) = &response.config_options { | ||
| dispatcher.emit_model_state_from_options(local_session_id, None, options); |
There was a problem hiding this comment.
Preserve provider identity when loading existing sessions
When replaying an existing session, model-state events are emitted with provider_id: None, so sessions without prior overlay metadata reopen with no provider attached in the UI. The chat view then falls back to a global/default provider, and the next prompt can re-prepare the ACP session with that fallback provider instead of the session’s original one, causing unintended provider switches and potential auth/model failures.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40308baff7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| format!("Failed to build ACP request body: {error}") | ||
| })?; | ||
| let response_value = connection | ||
| .ext_method(ExtRequest::new("goose/providers/list", params.into())) |
There was a problem hiding this comment.
Use
_goose namespace for ACP custom requests
This request uses "goose/providers/list", but custom ACP methods in this repo are declared under "_goose/..." (see crates/goose-sdk/src/custom_requests.rs and ui/acp/src/generated/client.gen.ts). Using the unprefixed name can return method-not-found from goose-acp, which breaks provider discovery; the same naming mismatch is also present for session export/import and working-dir update calls, so multiple ACP features can fail at runtime.
Useful? React with 👍 / 👎.
| .await | ||
| { | ||
| Ok(response) => response, | ||
| Err(_) => return Ok(None), |
There was a problem hiding this comment.
Propagate load_session errors instead of silently creating new sessions
This branch converts any load_session failure into Ok(None), and the caller then falls back to new_session. That means transient ACP/RPC failures are treated like “session not found,” silently forking the conversation into a brand-new session and losing continuity with the existing history. Only a real not-found condition should return None; other errors should be surfaced.
Useful? React with 👍 / 👎.
* add edge-case-finder agent skill Adds a QA-focused skill that analyzes branch changes to systematically find edge cases, error states, and untested user flow paths in UI code. Includes a comprehensive checklist reference covering empty states, error handling, async behavior, boundary conditions, input variations, navigation, permissions, and accessibility. * respect the selected change set when reading diffs The skill asked users to choose between committed and uncommitted changes but then only used main...HEAD commands. Add explicit commands for staged and unstaged diffs so the analysis matches the user's selection.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 491683ba3e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(prepared_session) = prepared_session { | ||
| let mut guard = state.lock().await; | ||
| guard.sessions.insert(composite_key, prepared_session); |
There was a problem hiding this comment.
Register local key when caching a newly prepared session
This stores a newly prepared session only under composite_key, but prepare_session_inner later relies on prepared_session_for_key(..., local_session_id) to reuse the same Goose session across persona-specific composite keys. On the first persona switch in a chat, the local fallback key is missing, so a second Goose session is created and conversation context is split unexpectedly. Use the existing dual-key registration path here as well so subsequent persona keys resolve to the same prepared session.
Useful? React with 👍 / 👎.
| dispatcher.clear_writer(&goose_session_id).await; | ||
| clear_cancel_requested(state, &composite_key).await; | ||
|
|
||
| if result.is_ok() && !canceled { | ||
| writer.finalize().await; |
There was a problem hiding this comment.
Drain pending prompt events before clearing writer
The writer is cleared immediately after prompt() returns, then acp:done is emitted; however this codebase already documents that ACP notifications are dispatched asynchronously after RPC completion. If late AgentMessageChunk/tool notifications are still queued, they no longer have a live writer and are dropped/misrouted after completion, truncating the assistant response intermittently under scheduler delay. Keep the writer attached until queued stream notifications are drained before finalizing.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d5ade3361
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let req = ForkSessionRequest::new( | ||
| session_id, | ||
| std::env::current_dir().unwrap_or_default(), |
There was a problem hiding this comment.
Use source working dir when forking a session
The fork path hard-codes std::env::current_dir() as the new session cwd, so duplicating a chat from History does not preserve the original project/worktree context and can execute subsequent tool calls in the app process directory instead. This is user-visible when the source session was tied to a different repo: the duplicated conversation opens, but follow-up prompts run against the wrong filesystem location until manually corrected.
Useful? React with 👍 / 👎.
…rning * origin/main: move agent skills to repo root for tool discoverability (#8535) port goose2 chat attachments into goose (#8534) chore(release): bump version to 1.31.0 (minor) (#8527) feature: provider & model config (#8515) Move goose2 (#8516) feat: onboarding UX for the TUI (#8513) Set MACOSX_DEPLOYMENT_TARGET=12.0 in build-cli.yml (#8525) Improve local inference settings and model downloader (#8467) Add prompt injection mitigation logging back (#8518) feat(providers): add llama-swap declarative provider (#8462) fix: Unable to Run `goose update` on Linux (#8465) Add vision/image support for local inference models (#8442)
…arsing Use sqlx's native DateTime<Utc> decoding for thread timestamp columns instead of fetching as String and manually parsing. This was missed when the baxen/goose2 branch was merged into main via PR #8516. The manual parsing silently falls back to Utc::now() on parse failure, which could produce incorrect timestamps. Letting sqlx decode directly is both more correct and simpler.
…arsing Use sqlx's native DateTime<Utc> decoding for thread timestamp columns instead of fetching as String and manually parsing. This was missed when the baxen/goose2 branch was merged into main via PR #8516. The manual parsing silently falls back to Utc::now() on parse failure, which could produce incorrect timestamps. Letting sqlx decode directly is both more correct and simpler. Signed-off-by: Matt Toohey <contact@matttoohey.com>
…arsing Use sqlx's native DateTime<Utc> decoding for thread timestamp columns instead of fetching as String and manually parsing. This was missed when the baxen/goose2 branch was merged into main via PR #8516. The manual parsing silently falls back to Utc::now() on parse failure, which could produce incorrect timestamps. Letting sqlx decode directly is both more correct and simpler. Signed-off-by: Matt Toohey <contact@matttoohey.com>
Moves block/goose2 into the main goose repo, plus a few small changes to get things running in the new location.