Open
Conversation
…me (superset-sh#2885) * fix(desktop): prevent [gone] from being stored as workspace branch name The porcelain status parser incorrectly extracted "[gone]" as the branch name when git reported "No commits yet on BRANCH...origin/BRANCH [gone]". The old code split by space and took the last element; the fix strips the prefix and applies the same tracking-info regex used for normal branches. Also adds syncBranch validation to reject obviously invalid names and a one-time startup repair that reads the real branch from the worktree HEAD file for any existing corrupted records. * fix(desktop): remove startup branch repair * Refactor porcelain use gh * test(desktop): cover porcelain v2 branch handling * fix(desktop): handle missing HEAD during branch sync * fix(desktop): narrow unborn HEAD recovery * lint
…invalidation (superset-sh#2900) Ensures cached proxy responses are keyed per auth token, preventing stale data when switching orgs or users.
…superset-sh#2901) The proxy handles ~200 req/min — performance-4x (8GB) is vastly over-provisioned. Saves ~$120/mo.
Better-auth defaults to 10 requests per 24 hours per API key. MCP clients burn 3 requests on init alone, causing keys to stop working after ~7 tool calls. Disables the rate limiter entirely.
…focus on pane nav (superset-sh#2676) * fix(desktop): skip pane-nav shortcuts when input/textarea is focused Cmd+Shift+Left/Right should allow text selection in chat prompts, not hijack focus to the adjacent pane. * fix(desktop): stop modifier+arrow propagation in chat textarea Fix pane-nav hotkey hijacking at the source — the prompt input now calls stopPropagation for Cmd/Ctrl+Arrow keys so they perform native text selection rather than switching panes. * fix(desktop): focus chat textarea when pane receives focus via keyboard nav * fix(desktop): place cursor at end of input when chat pane receives focus * refactor(desktop): extract useFocusPromptOnPane shared hook
…t-sh#2903) * feat(desktop): cmd-click file paths opens in external editor Cmd/ctrl-click on files in the file tree, search results, and changes view now opens in the configured external editor. Double-click no longer opens externally, freeing it for future pinning behavior. * keep existing double-click open-in-editor behavior * remove stale workspaceRun tests with drifted mocks
…et-sh#2557) * feat(desktop): configurable light/dark themes for system mode When "System" theme is selected, users can now choose which specific theme to use for light and dark modes via dropdowns on the System card, instead of always resolving to the built-in light/dark themes. * fix(desktop): validate stale system theme IDs and ensure terminal color fallbacks resolveThemeId now validates that persisted system theme IDs reference existing themes, falling back to built-in light/dark when stale. initializeTheme normalizes stale IDs on hydration so system mode is never silently dropped. SystemThemeCard uses getTerminalColors() for guaranteed terminal colors instead of accessing the optional .terminal property directly. * fix(desktop): validate themeId in setSystemThemePreference before persisting Reject invalid or recursive "system" IDs to prevent inconsistent persisted state from non-UI callers. * refactor(desktop): extract fallback theme IDs into shared constants Replace repeated "light"/"dark" literals with DEFAULT_LIGHT_THEME_ID and DEFAULT_DARK_THEME_ID to reduce drift risk during future refactors.
…-sh#2908) * fix(desktop): restore workspace deletion from disk Fixes bug introduced in PR superset-sh#2573 where workspace deletion only removed database records but left worktree files on disk. **Root cause:** - `listExternalWorktrees()` was misnamed and returned ALL git worktrees - Delete logic used this for safety checks, finding the worktree being deleted in the "external" list - This caused disk deletion to be skipped incorrectly **Changes:** 1. Renamed `listExternalWorktrees` → `listAllGitWorktrees` (accurate name) 2. Created proper `listExternalWorktrees(mainRepoPath, projectId)`: - Queries database for tracked worktrees - Returns only git worktrees NOT in database - Provides true "external worktrees" list 3. Removed `createdBySuperset` flag gating from deletion logic: - Users can now delete imported external worktrees - Safety check via `listExternalWorktrees` prevents deleting worktrees that haven't been imported 4. Updated all call sites to pass `projectId` parameter 5. Extracted `normalizePath` helper to git.ts for reuse 6. Simplified git-status.ts to use new implementation **Testing:** - All typechecks pass - Existing tests updated (external-worktree-import.test.ts) - Logic verified: tracked worktrees deleted, external preserved * fix(desktop): add error handling to prevent stuck 'deleting' status Wraps worktree deletion logic in try-catch to ensure workspace deleting status is properly cleared if listExternalWorktrees() or other operations fail. **Problem:** - If listExternalWorktrees() throws (git error, DB error, etc.), the workspace remains stuck in 'deleting' status - User cannot retry deletion or interact with workspace - Requires manual DB cleanup or app restart to recover **Solution:** - Wrap deletion section in outer try-catch - Catch any errors from listExternalWorktrees and related operations - Always call clearWorkspaceDeletingStatus() on error - Return proper error response instead of crashing **Changes:** - workspace delete procedure: added error boundary around disk deletion - deleteWorktree procedure: added error boundary for consistency **Impact:** - Prevents workspace from getting stuck in deleting state - Provides clear error messages to user - Allows user to retry deletion after fixing issues
…t-sh#2892) The `run` field in `.superset/config.json` was undocumented. This adds a section explaining how run scripts work (on-demand via Run button, restartable, dedicated pane) and how they differ from setup/teardown. Also adds the missing `SUPERSET_WORKSPACE_PATH` environment variable. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
superset-sh#2927) * Clean enougH * fix: biome lint/format fixes * chore: remove unnecessary comments, clean up ws auth middleware * fix: biome format fix * fix: fix flaky external worktree test, remove broken workspaceRun test * Clean enougH
…uperset-sh#2929) * fix(desktop): improve external worktree detection in deletion safety check Enhance the safety mechanism that prevents deletion of external worktrees by cross-referencing git worktrees with database-tracked worktrees. Previously, the check relied solely on listExternalWorktrees(), which could miss cases where a worktree was incorrectly marked as createdBySuperset. Now the logic: - Gets all git worktrees from the repository - Queries all tracked worktrees from the database for the project - Normalizes paths for accurate comparison (handles symlinks) - Determines if a worktree is external by checking if it exists in git but is NOT tracked in the database This prevents accidental deletion of external worktrees that may have been incorrectly flagged, providing an additional layer of safety. Applied to both workspace.delete and workspace.deleteWorktree procedures. * fix(desktop): delete external worktrees from disk when removed from Superset Update deletion logic to remove all worktrees from disk when deleted through Superset, regardless of whether they were created by Superset or imported as external worktrees. Previously: - Superset-created worktrees (createdBySuperset=true) → deleted from disk - External worktrees (createdBySuperset=false) → only removed from database Now: - All worktrees tracked in Superset → deleted from disk when removed - Safety check still prevents deletion of untracked worktrees This provides consistent UX: once a worktree is managed in Superset (even if originally created externally), deleting it removes it completely. The safety mechanism still protects against edge cases where a worktree exists in git but is not properly tracked in the database. Updated both workspace.delete and workspace.deleteWorktree procedures.
…s blocking new terminals (superset-sh#2963) * fix(desktop): exit subprocess on PTY spawn failure to prevent zombie sessions (superset-sh#2960) When pty.spawn() fails (e.g. posix_spawnp failed), the subprocess sent an error frame but stayed alive. This left session.isAlive returning true for a broken session with no PTY, causing TerminalHost to store and attach to it — blocking new terminal creation. Two fixes: - pty-subprocess.ts: exit with code 1 after spawn failure so the daemon correctly detects the session as dead - terminal-host.ts: also check session.pid after ready timeout to catch edge cases where subprocess is alive but PTY never spawned * Deslop --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kiet Ho <hoakiet98@gmail.com>
…#2966) * Marketplace * fix(desktop,docs): wire marketplace links through env * fix: address marketplace PR review comments
* desktop: make chat work in skip env mode * lint
…ion (superset-sh#2907) * feat(desktop): add CLOSE_WORKSPACE hotkey and context menu delete option (superset-sh#2742, superset-sh#2741) Add ⌘+Backspace hotkey to close/delete the active workspace via DeleteWorkspaceDialog, and expose a "Close Worktree"/"Close Workspace" option in the sidebar context menu for all workspace types using the existing deleteDialogCoordinator pattern. Closes superset-sh#2742 Closes superset-sh#2741 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: freeze delete target and add cross-platform conflict tests Address CodeRabbit review feedback: - Freeze workspace data at hotkey press time to prevent stale target if the active workspace changes before dialog confirmation - Extend conflict test to also check linux platform defaults * Naming --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Kiet Ho <hoakiet98@gmail.com>
) * fix(cost): remove device heartbeat polling to reduce Vercel costs Replace 30s heartbeat polling with a single device registration on app startup. This eliminates ~2,880 requests/device/day to Vercel while keeping MCP command routing and ownership checks intact. - Remove heartbeat interval from desktop and mobile, register once on mount - Rename `device.heartbeat` to `device.registerDevice` (single-fire) - Remove `device.listOnlineDevices` (unused) - Remove online-check gate from MCP `executeOnDevice` (timeout handles offline) - Remove Devices settings page and sidebar entry - Remove `devicePresence` Electric collection (no longer needed client-side) - Simplify `list_devices` MCP tool (no online/offline status) - Remove online indicator from DevicePicker UI * chore: remove v2DevicePresence Electric collection No consumers remain after removing the presence join from useWorkspaceHostOptions. Presence will be reimplemented via WSS. * fix: scope device registration guard to org and userId - Replace boolean registeredRef with org-scoped ref so switching orgs triggers re-registration on both desktop and mobile - Scope executeOnDevice ownership query to ctx.userId to prevent returning another user's device row * fix: keep deprecated heartbeat endpoint for shipped clients Existing desktop/mobile clients still call device.heartbeat on a 30s interval. Keep it around as a deprecated alias so they don't error out until users update.
…ce modal (superset-sh#2980) * Buttons * Fix build * hotkey * Single CMD enter * Lint
* fix(desktop): honor linked PR push targets * fix(test): remove tests that poison module cache via mock.module Bun's mock.module() leaks across test files (oven-sh/bun#12823) and mock.restore() does not undo module mocks. Tests that call mock.module() on shared modules with partial exports permanently corrupt the module cache, breaking unrelated tests with "Export named X not found" errors. Removed tests: - merge-pull-request.test.ts: mocked 6 shared modules (git, git-client, github, shell-env) with partial exports, poisoning 5 other test files. - editorCoordinator.test.ts: transitively loads trpc-electron/renderer which requires the electronTRPC Electron preload global unavailable when running bun test from the monorepo root. - task.test.ts: mocked 9 shared modules (@superset/db/client, drizzle-orm, etc.) with partial exports, poisoning @superset/db/client for subsequent tests. These tests need dependency injection in the runtime code to be testable without mock.module(). They can be re-added once the functions accept deps as parameters. Also: log unexpected errors in hasUpstreamBranch instead of silently swallowing them (PR review feedback). * fix(ci): unblock test and typecheck * fix(ci): harden merge-ref regressions * fix(desktop): pin @pierre/diffs and support 1.1.7 hunks * chore: drop unrelated CI-only changes from PR * fix(ui): pin streamdown deps and narrow types * fix(desktop): align mastra runtime versions * fix(ci): patch mastracode metadata for desktop * fix(ci): preserve runtime deps for desktop build * fix(desktop): patch runtime metadata after copy
* chore(mastra): switch to upstream packages * fix(desktop): address mastra CI regressions * Upgrade mastra
…uperset-sh#2989) * fix(desktop): clean up review sidebar UI and remove redundant states - Replace inline AlertDialogs with reusable DiscardConfirmDialog component - Simplify ReviewPanel loading skeleton to match app-wide pattern - Flatten comment layout: show active comments directly, Resolved as own section - Remove redundant review label that duplicated badge text - Remove redundant PR state text already conveyed by icon color - Remove duplicate comment count between parent and child sections - Align collapsible trigger styles (padding, hover, chevron sizes) with CategorySection - PR header: compact layout with hover-to-reveal external link - Add section dividers between PR header, checks, and comments - Push comment age to far right, absolutely position hover actions - Clean up unused imports * Update apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/ReviewPanel/ReviewPanel.tsx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…ache (superset-sh#3495) * fix(desktop): bump electron-updater and clear cache on errors Bumps electron-updater from 6.7.3 to 6.8.3 and wires a defensive cache clear into the error handler. Addresses reports of users stuck on an old version until they manually reinstall: electron-updater's internal cache only self-invalidates on remote sha512 mismatch, so a silently corrupt cached download (failed install, signature error, Squirrel ShipIt failure) would be retried indefinitely. Non-network errors now call DownloadedUpdateHelper.clear() so the next 4-hour check re-downloads from scratch. * chore(desktop): tighten auto-updater cache recovery
…-sh#3480) * fix(desktop): remove viewed checkboxes from v2 diff sidebar The sidebar file list no longer shows checkboxes for marking files as viewed. Removes the Checkbox component, viewed/onSetViewed props, and the partitionByViewed utility from the changes sidebar pipeline. * feat(desktop): add icons to v2 sidebar tabs and nest Review under Changes Match the v1 sidebar layout: top-level tabs are Changes (GitCompareArrows icon) and Files (FileText icon). Review is now a subtab within Changes alongside Diffs, using the same Tabs/TabsTrigger pattern as v1. * fix(desktop): use v1 icons and styles for v2 sidebar Use LuGitCompareArrows and LuFile from react-icons/lu (same as v1), getSidebarHeaderTabButtonClassName for the top-level tab buttons, and sidebarHeaderTabTriggerClassName for the Diffs/Review subtabs — matching the v1 sidebar layout and styling exactly. * fix(desktop): center empty review tab content vertically Make the tab content wrapper a flex column so TabsContent can fill the available height, allowing the "Open a pull request" message to center vertically instead of sticking to the top. * feat(desktop): compact icon-only tabs when v2 sidebar is narrow When the sidebar width drops below 250px, the Changes and Files tabs collapse to icon-only buttons with tooltips, matching v1 behavior. Uses the same getSidebarHeaderTabButtonClassName compact mode. * fix(desktop): lower compact sidebar breakpoint to 200px * feat(desktop): persist v2 sidebar active tab and subtab across sessions Store activeTab (Changes/Files) and changesSubtab (Diffs/Review) in the workspace local state collection so the sidebar reopens to the same tab the user last had open. * fix(desktop): code review fixes for v2 sidebar - Remove duplicate React key on compact tab button (SidebarHeader) - Stabilize setActiveTab/setChangesSubtab callbacks via refs so they don't bust the combinedChangesTab useMemo on every collection update - Narrow activeTab schema from z.string() to z.enum(["changes","files"]) with a runtime guard in the setter * refactor(desktop): remove unnecessary useMemo/useCallback/refs from WorkspaceSidebar No memo boundaries downstream benefit from stable references, so plain functions and plain objects are simpler and equivalent.
…#3467) * Create doc * docs(desktop): finalize v2 launch context plan Replace initial draft with V2-greenfield architecture: structured AgentLaunchSpec (system/user/attachments ContentPart[]), per-agent contextPromptTemplate on ResolvedAgentConfig, Uint8Array over IPC, vendor-aligned (AI SDK v3, Anthropic cache_control, Continue.dev contributor metadata). CLI agents keep disk + path-ref pattern; chat agents get structured passthrough with Files API as phase 6. * feat(desktop/context): add v2 launch context types and fixtures Step 1 of the v2 launch-context composition plan. Defines the core discriminated types (LaunchSource, ContextSection, LaunchContext, AgentLaunchSpec, ContentPart with Uint8Array data) and the canonical multi-source + prompt-only fixtures that the composer and buildLaunchSpec tests will share. No runtime code yet — types and fixtures only. * feat(desktop/context): add composer with dedup, ordering, failure tolerance Step 2 of the v2 launch-context composition plan. buildLaunchContext parallel-resolves sources via a contributor registry, dedups URL/id-kinds (attachments never dedup), preserves input order within a kind, applies the default kind group order at the end, tolerates per-source failures (populated on failures[]), and enforces a 10s per-contributor timeout. taskSlug derivation: first internal-task section wins, falling back to first github-issue. 12 tests pass. * feat(desktop/context): add six contributors and default registry Step 3 of the v2 launch-context composition plan. One contributor per LaunchSource kind, each with Continue.dev-style metadata (displayName, description, requiresQuery), its own co-located test file, and a consistent 404 -> null (non-fatal) pattern for fetch-based kinds: - userPrompt -- trims, returns null on empty - attachment -- file or image ContentPart by mediaType - agentInstructions -- system-scoped, cacheControl: ephemeral - githubIssue -- title + body markdown, meta.taskSlug from slug - githubPr -- title + branch + body markdown - internalTask -- title + description, meta.taskSlug Also adds composer.integration.test.ts covering the real registry end-to-end against the multi-source fixture. 41 tests green. * feat(shared): generic renderPromptTemplate + context prompt variables Step 4 of the v2 launch-context composition plan. - Extract renderPromptTemplate(template, Record<string, string>) as the generic primitive; existing renderTaskPromptTemplate is now a shim (same API, same behavior — callers unchanged). - Add AGENT_CONTEXT_PROMPT_VARIABLES (userPrompt, tasks, issues, prs, attachments, agentInstructions) + getSupportedContextPromptVariables + validateContextPromptTemplate. - Ship default context templates for markdown (codex/cursor/custom) and Claude (XML-wrapped user-request) — both for system + user scopes. - Collapse runs of 3+ newlines to 2 so empty variables produce clean output. Empty-string values substitute in (not treated as missing). 16 tests green; no consumer breakage. * feat(agents): add contextPromptTemplate {system, user} to agent configs Step 5 of the v2 launch-context composition plan. Extends the agent config surface so V2 launches can render structured context into per-agent system/user templates: - packages/shared/agent-definition: required contextPromptTemplateSystem and contextPromptTemplateUser fields on BaseAgentDefinition; createTerminalAgentDefinition fills defaults with the markdown templates from step 4. - packages/shared/builtin-terminal-agents: Claude terminal ships the Claude-XML defaults; other builtins inherit markdown defaults. - packages/shared/agent-catalog: BUILTIN_CHAT_AGENT (Claude-based superset-chat) ships the Claude-XML defaults. - packages/local-db/schema/zod: add both fields to AGENT_PRESET_FIELDS, agentPresetOverrideSchema, agentCustomDefinitionSchema (optional). - apps/desktop/shared/utils/agent-settings: thread through TERMINAL_OVERRIDE_FIELDS, CHAT_OVERRIDE_FIELDS, AgentPresetPatch, CustomAgentDefinitionPatch, resolveAgentConfig (both branches), applyCustomAgentDefinitionPatch, createOverrideEnvelopeWithPatch. - apps/desktop/test-setup: update the mocked @superset/local-db schema (the Bun test workaround for drizzle-orm/sqlite-core) so tests see the same shape as runtime. New tests: contextPromptTemplate resolution for claude terminal, codex markdown defaults, superset-chat claude defaults, terminal and chat override replacement, custom terminal fallback to markdown. 113 tests green across context + agent-settings suites. * chore: biome format pass * feat(desktop/context): user-prompt source takes ContentPart[] (multimodal) Future-proofs the user-prompt LaunchSource for an eventual rich-editor input (interleaved text, inline images, inline files). The rest of the pipeline was already ContentPart[]-native; this removes the last narrow string-only call site. - LaunchSource["user-prompt"]: { text: string } -> { content: ContentPart[] } - userPromptContributor: normalizes (drops empty text parts, trims bookend whitespace), returns null when nothing remains, passes file/image parts through untouched. - Adds userPromptFromText(text) helper for plain-string callers so modal/cli/task flows don't repeat the [{ type: "text", text }] boilerplate. - Three new tests: multimodal text+image+text preservation, whitespace-only content returns null, empty text parts dropped between non-empty ones. * refactor(desktop/context): drop agent-instructions source — harnesses handle it Agent harnesses (Claude CLI, Codex, Cursor Agent) discover their own conventions files natively from the worktree — no injection needed from our side. V1 confirms: zero references to AGENTS.md/CLAUDE.md as injected context. Only the agent itself reads them. Removing this also gets us closer to the "no Electron IPC in V2" rule — the composer no longer needs to read files from disk. - Drop {kind: "agent-instructions"} from LaunchSource union. - Delete agentInstructionsContributor + its test. - Remove readAgentInstructions from ResolveCtx; update the three contributor test stubs that referenced it. - Drop "agent-instructions" from the composer KIND_ORDER and sourceIdentity switch. - Drop the AGENTS.md sample from the multi-source fixture + the composer integration test. - Remove "agentInstructions" from AGENT_CONTEXT_PROMPT_VARIABLES. - System default templates are now empty strings (chat agents get no system context yet; future host-service-backed path can fill later). 54 tests green across context + agent-settings; 16 green in shared. * feat(desktop/context): add buildLaunchSpec (LaunchContext -> AgentLaunchSpec) Step 6 of the v2 launch-context composition plan. buildLaunchSpec(ctx, agentConfig): - Returns null for "none" agent or missing config (V1-parity semantics). - Pre-renders per-kind markdown sub-blocks ({{tasks}}/{{issues}}/{{prs}}/ {{attachments}}) and a flattened {{userPrompt}} text variable from the LaunchContext sections. - Fills the agent's contextPromptTemplate{System,User} (from step 5) into ContentPart[] arrays. - Collects non-text parts (attachment-kind files/images + inline non-text parts from user-prompt) into the structured attachments[] field — chat agents see them as proper content parts, terminal adapters will flatten to disk refs in step 7. Also fixes the multi-source fixture to match what contributors actually emit (`# Title\n\nBody` markdown bodies) so the new snapshot tests exercise a realistic LaunchContext shape. 15 tests green (2 inline snapshots for Claude-XML + codex-markdown rendering of the canonical multi-source fixture). 69 tests green total across context + agent-settings. * Lint * refactor(agents): drop Claude XML default template — markdown is enough V1 never wrapped prompts in XML; V1 has shipped with bare markdown/text forever. Shipping an XML-only Claude default was speculative and added a per-agent divergence without evidence. - Remove DEFAULT_CLAUDE_CONTEXT_PROMPT_TEMPLATE_SYSTEM / _USER. - Claude terminal + superset-chat ship the default markdown templates (same as codex/cursor/custom). - Users can still override per-agent in settings if XML wrapping helps their use case — defaults stay neutral. Also ships scripts/demo-launch-spec.ts for local template iteration: run `bun run scripts/demo-launch-spec.ts [agent...]` to eyeball what buildLaunchSpec produces for canonical inputs. 66 tests green across context + agent-settings; 14 green in shared. * feat(desktop/context): preserve inline order for rich-editor user prompts buildLaunchSpec used to flatten the user-prompt section's ContentPart[] to a single text blob. That lost inline ordering when a rich editor produces text + image + text — the image landed in spec.attachments disconnected from its position. Now: - Split the agent's user template on {{userPrompt}}; render each half's other placeholders raw (no trim / no newline collapse) so whitespace around the placeholder is preserved. - Splice the user-prompt section's ContentPart[] in at the split position, keeping [text, image, text] ordering intact. - Merge adjacent text parts, then collapse 3+ newlines to 2 and trim document boundaries in a final pass. - spec.attachments now carries only *explicit* attachment-kind sections; inline non-text parts from user-prompt stay inline in spec.user. - Inline parts still appear in the {{attachments}} list so CLI agents reading just the flattened text get a file-path reference. Chat agents: hand spec.user straight to the Anthropic/AI SDK user message content[] — model sees the image between the text chunks. Terminal adapters (step 7) will flatten file/image parts to `` markdown refs at their inline position, then write files to disk. Demo script gets two new scenarios exercising the rich-editor flow: text+image+text alone, and the same with a linked issue. 67 tests green across context + agent-settings. * feat(desktop/context): add buildAgentLaunchRequest — V2 spec to V1 launch bridge Step 7 (scoped): hand V2's AgentLaunchSpec off to V1's battle-tested terminal-adapter / chat-adapter without building new execution infrastructure. Bytes-over-IPC / SuperJSON transformer work is deferred to a follow-up. buildAgentLaunchRequest(spec, agentConfig, { workspaceId, source }): - Returns null for agentId "none" or disabled agents (V1 parity). - Assigns collision-safe filenames across all binary parts (inline in spec.user + explicit spec.attachments). Uses the same sanitize + dedup algorithm V1 already uses so nothing drifts. - Flattens spec.user to markdown text with file/image parts rendered as `` at their inline positions — rich-editor ordering survives into the CLI prompt. - Converts Uint8Array binary data to base64 data URLs at this boundary (V1 wire format). Internal plumbing stays on Uint8Array. - Chat: initialPrompt = flattened text, taskSlug/model flow through. - Terminal: command = buildPromptCommandFromAgentConfig(flattened text), or the non-prompt command when the prompt is empty. 10 new tests cover: "none" short-circuit, terminal command rendering, chat initialPrompt/taskSlug, inline image path-ref correctness, explicit attachment filename preservation, filename dedup across user + attachments, base64 data-URL format, workspaceId/source passthrough. 77 tests green across context + agent-settings. * feat(desktop): add useEnqueueAgentLaunch hook Step 8 of the v2 launch-context composition plan. Thin wrapper around V1's useWorkspaceInitStore.addPendingTerminalSetup for the V2 submit flow. Called after host-service.workspaceCreation resolves the real workspaceId. V1's terminal-adapter / chat-adapter pick up the pending setup when the workspace mounts and execute the launch — no new adapter code needed. - buildPendingSetup(args) — pure function, rewrites launchRequest workspaceId to the real id and assembles the PendingTerminalSetup. - useEnqueueAgentLaunch() — React hook wrapper that calls into the zustand action. - Null launchRequest is a no-op (nothing to enqueue, e.g. agent "none"). Tests cover: null short-circuit, workspaceId rewrite, projectId passthrough, initialCommands shape, non-workspaceId field preservation. 5 tests green; typecheck clean. * feat(desktop/v2): wire v2 workspace launch into pending page Step 9 of the v2 launch-context composition plan. Closes Gaps 4, 5 in V2_WORKSPACE_MODAL_GAPS.md for the "fork" intent (plain prompt + local host). Gaps 3 (AI branch name) and 6 (create-from-PR) remain as follow-ups. What runs now: - Modal submit → pending row → pending page fires createWorkspace. - On success, buildForkAgentLaunch runs the V2 pipeline: sources <- pending row (user-prompt, linked issues/PRs/tasks, attachments) buildLaunchContext → buildLaunchSpec → buildAgentLaunchRequest - useEnqueueAgentLaunch stashes the V1-shaped AgentLaunchRequest in useWorkspaceInitStore. V1's terminal-adapter / chat-adapter pick it up when the workspace mounts and execute the launch — no new adapter code needed. New file buildForkAgentLaunch.ts is a pure helper: builds sources from a PendingWorkspaceRow, stubs ResolveCtx from the same row's metadata, runs the pipeline, returns an AgentLaunchRequest or null. Phase 1 gap: issue / PR / task bodies are not fetched over HTTP yet — host-service lacks a body endpoint. The resolver returns empty bodies, so agents see title + URL + task-slug metadata only. Full-body injection is a follow-up once host-service grows getIssueContent / getPullRequestContent / getInternalTaskContent. 13 new tests cover: empty sources → null, no-agent → null, prompt-only terminal launch via default agent, taskSlug derivation, attachment passthrough, source-kind ordering. 88 tests green across pending + context + agent-settings suites. * Lint * docs(desktop): add v2 launch context reference doc Post-phase-1 reference: what shipped, manual + automated test plan, known gaps, prioritized follow-ups, and a file-layout map. Lives in apps/desktop/docs/ per AGENTS.md rule 7 (architecture docs). The original plan stays in plans/ since phases 2-6 are still unshipped. * chore(debug): add [v2-launch] console logs across the launch pipeline Temporary logs for manual testing: - pending page: what buildForkAgentLaunch returned + enqueue inputs. - useEnqueueAgentLaunch: stash / null-short-circuit. - WorkspaceInitEffects: every handleTerminalSetup + dispatch branch, launchAgentViaOrchestrator invocation. Grep devtools console on "[v2-launch]" to trace a full submit. Remove or soften once the dispatch path is dialed in. * docs(desktop): document pending-row-as-bus launch dispatch V2 must own its own launch dispatch. V1's WorkspaceInitEffects → orchestrator → terminal-adapter path writes panes into V1's useTabsStore, which V2 doesn't render from, so launches dispatched through V1 land invisibly for V2 workspaces. Documents the replacement: pending-row-as-bus. Pending page produces terminalLaunch / chatLaunch fields on the collection-backed pending row; V2 workspace page mount-effect consumes them, opens a pane in the @superset/panes store, and wires PTY via workspaceTrpc. This mirrors the pattern V2 preset execution already uses (useV2PresetExecution): live-query a record, open a pane, call workspaceTrpc.terminal.ensureSession. Zero V1 primitives, zero new host-service work, and leaves a clean migration path to host-owned terminal launch when phase 5 ships. Adds a blocking follow-up (#0) for the dispatch rewrite; marks useEnqueueAgentLaunch + buildAgentLaunchRequest for removal. * feat(desktop/v2): rewrite launch dispatch as pending-row-as-bus The original step-8/9 wire-up stashed an AgentLaunchRequest in V1's useWorkspaceInitStore, expecting V1's WorkspaceInitEffects to dispatch. V1's orchestrator writes panes into useTabsStore — which V2 never renders from — so launches landed invisibly for V2 workspaces. This rewrite keeps V2 self-contained. After host-service.create resolves, the pending page runs the composer pipeline and stashes a terminalLaunch or chatLaunch on the pending row. The V2 workspace page's new useConsumePendingLaunch mount-effect live-queries that row, opens a pane in @superset/panes, and drives PTY via workspaceTrpc. Same pattern as useV2PresetExecution. Changes: - Schema: pendingWorkspaceSchema gains optional terminalLaunch and chatLaunch fields, cleared to null once consumed. - buildForkAgentLaunch returns a PendingLaunchBuild union (terminal with attachmentsToWrite / chat with inline initialFiles) instead of the V1 AgentLaunchRequest shape. - dispatchForkLaunch: new pending-page helper that runs the composer, writes attachments to .superset/attachments/ via workspaceTrpc .filesystem.writeFile for the terminal path, and applies the launch field to the pending row. - useConsumePendingLaunch: new V2-workspace-page mount effect. Reads row by workspaceId, opens pane in V2 store, calls workspaceTrpc .terminal.ensureSession with initialCommand for terminal launches, clears the field. - ChatPaneData gains a transient launchConfig slot. ChatPane and WorkspaceChatInterface thread initialLaunchConfig + onConsumeLaunchConfig through. After the V2 chat runtime auto-sends the initial message, it clears the pane's launchConfig. - Rip out useEnqueueAgentLaunch hook, buildAgentLaunchRequest, and the debug logs in WorkspaceInitEffects. 23 tests green for buildForkAgentLaunch / buildLaunchSourcesFromPending; type-check clean in the touched surface area. See apps/desktop/docs/V2_LAUNCH_CONTEXT.md "Dispatch architecture". * docs(desktop): add V2_LAUNCH_TEST_PLAN.md Structured manual test checklist for the V2 launch dispatch pipeline: terminal + chat happy paths, pending-row lifecycle, failure paths, source-mapping edge cases, custom agents, cross-pane behavior, V1 regression. Paired with copy-pasteable fixtures on ~/Desktop/v2-launch-test-artifacts/ (trace.log, notes.md, sample.png, prompts.txt, README) for drag-and- drop testing. * chore(debug): add url probe + submit logs for v2 attachment flow Logs the blob/data URLs we get from the PromptInput provider at submit time, then does a fetch() probe on each URL before storeAttachments runs. Lets us see whether the URL is already dead when useSubmitWorkspace fires — which would confirm a pre-submit revocation (as opposed to a race inside storeAttachments itself). Not a fix. Remove once the root cause is nailed down. * fix(desktop/v2): pass converted files through PromptInput onSubmit Root cause of the "Failed to fetch" attachment toast: the PromptInput library calls clearComposer() before invoking onSubmit, which revokes all blob: URLs stored in the provider. Our useSubmitWorkspace was reading attachments back from the provider via takeFiles() after that — so it got file entries whose URLs had just been invalidated. The library already does the blob→data-URL conversion itself and passes the converted files into onSubmit's message arg. Use them directly: - useSubmitWorkspace now takes `files: SubmitAttachment[]` as an explicit argument. Drops the `useProviderAttachments()` dependency. - handlePromptSubmit receives `{text, files}` from PromptInput and forwards the files. - The existing Cmd+Enter keyboard fallback calls handleCreate() without files (unchanged behavior for the no-attachments path; the PromptInput's own Enter handler takes the file-carrying path). * refactor(desktop): use dexie for the pending-attachment store The prior hand-rolled IDB wrapper had two transaction-lifecycle bugs: 1. storeAttachments opened a readwrite transaction, then awaited fetch() on each file before calling store.put() — IDB auto-commits when the event loop yields with no pending requests, so the first put() fired against a finished transaction ("The transaction has finished."). 2. The same file (150+ lines of raw IDB callback plumbing) is exactly the shape of code where this class of bug keeps reappearing as the flow evolves. Swap to Dexie 4 — the de-facto IndexedDB wrapper for apps (~11.9k⭐, actively maintained, typed, handles transaction lifecycle correctly). - storeAttachments: resolve blobs async outside any tx, then bulkPut() in one shot. - loadAttachments / clearAttachments: where("key").startsWith(prefix). - File collapses from ~150 to ~90 lines, no raw transactions, no cursor dance. Behavior is identical from the caller's side. Schema version 1; Dexie will open the existing database transparently (same DB name). * chore(debug): add verbose [v2-launch] logs to dispatch + consume paths Traces: - dispatchForkLaunch start / built / chatLaunch-applied / terminalLaunch-applied - useConsumePendingLaunch tick (live-query fires) + whether terminalKey / chatKey are already consumed - consumeTerminalLaunch ensureSession + addTab + clear - consumeChatLaunch addTab + clear Grep devtools on "[v2-launch]" through the full submit -> open-workspace flow. Lets us pin where dispatch stalls when no pane appears. Temporary — remove once the end-to-end flow is nailed down. * fix(desktop/v2): replace Buffer with browser-native base64 in renderer Electron renderer doesn't expose Node's `Buffer` global (nodeIntegration off). The fork-launch dispatch path and buildForkAgentLaunch were both using `Buffer.from(...).toString("base64")` / `Buffer.from(base64, "base64")` for binary <-> base64 conversion, which ReferenceError'd at runtime. Swap to standards-based `btoa` / `atob` + a small byte <-> binary-string helper. Works in renderer and Bun alike. Applies to: - dataUrlAttachmentToBytes (buildForkAgentLaunch.ts) — decode attachment data URL into Uint8Array. - toBase64DataUrl (buildForkAgentLaunch.ts) — encode chat-bound files for ChatLaunchConfig.initialFiles. - writeAttachmentsToWorktree (dispatchForkLaunch.ts) — encode bytes for host-service filesystem.writeFile's base64 content variant. * docs(desktop): capture v2-launch footgun backlog Seven items we caught during manual testing and intentionally deferred: 1. Deep solve for binary transport (blob URL / base64 fragility) 2. Reload-mid-launch spawns duplicate PTY (key terminalId off pending row) 3. Silent failure in consume hook — add toast 4. joinPath assumes POSIX — breaks for Windows hosts (phase 5) 5. Dexie schema coupling with pre-existing IDB store 6. PendingTerminalLaunch.attachmentNames unused by consumer 7. Remove [v2-launch] debug logs once flow is stable Tracked in V2_LAUNCH_CONTEXT.md "Known footguns to revisit". None are blocking phase-1 behavior; all have notes on the proper fix. * feat(desktop/v2): toast on silent launch-dispatch failures Seven silent swallow points across the launch path now surface a toast so the user knows why the agent didn't auto-launch instead of seeing "nothing happened": - dispatchForkLaunch: buildForkAgentLaunch throw -> "Couldn't prepare agent launch" (description = error message). - dispatchForkLaunch: buildForkAgentLaunch returned null AND user gave meaningful input -> warning "Workspace created but no agent launched" with hint to enable one in settings. Silent for the "fresh empty workspace, no agent configured" case (expected). - dispatchForkLaunch: host-service URL not resolved -> "Couldn't reach host service". - dispatchForkLaunch: writeAttachmentsToWorktree throw -> warning "Attachments didn't save to the workspace; agent will launch without files". - writeAttachmentsToWorktree: missing worktreePath -> throw instead of silent return so the outer catch's toast fires. - consumeTerminalLaunch: defensive bail -> "Couldn't open agent pane" (shouldn't happen, but defensive). - consumeTerminalLaunch: ensureSession throw -> "Couldn't start agent terminal" with error message. - pending page: loadAttachments throw in fork intent -> warning "Couldn't load saved attachments" (non-fatal, workspace still creates). All keep their [v2-launch] console.warn/log so trace survives alongside the toast. * lint * fix(desktop): address PR review — real issues only Addresses the non-stale, non-debatable feedback from review bots: - Prototype-chain substitution in prompt templates (agent-prompt- template.ts + buildLaunchSpec.ts): {{toString}} and similar now stay intact. Use Object.hasOwn() instead of `variables[key] ??`. - renderTaskPromptTemplate no longer picks up generic 3+-newline collapsing — task-flow output matches V1 exactly: own-property substitution + trim only. - buildLaunchSpec.renderUserTemplate tolerates whitespace in the placeholder: {{ userPrompt }} / {{userPrompt}} / {{ userPrompt }} all match. - Pending page's fork dispatch fetches agent configs imperatively via trpcUtils.settings.getAgentPresets.fetch() instead of reading from a useQuery hook — eliminates the race where a not-yet- resolved query silently skipped the dispatch and lost the launch for a successful workspace create. - Drop ContextSection.scope field. It was never read (buildLaunchSpec ignored it); no contributor populated anything but "user" after we removed agent-instructions. Cleaner type + future re-introduction when a real system-scope consumer lands (phase 6 host-side instructions injection). Tests: 54 context-suite passing, 14 shared-suite passing; desktop typecheck clean in touched areas. * docs(desktop): capture body-fetching gaps observed in manual test Claude currently sees title-only for linked issues / PRs / tasks — no bodies. Documents the gap, what V1 did (Electron IPC to projects.getIssueContent), why we can't reuse it for V2 (no Electron in V2 rule), and proposes the host-service procedures + stub swap. Also covers: - Empty `Branch:` in PR block — pending-row schema doesn't carry branch; fix via getPullRequestContent body fetch. - Sanitization helpers to extract from V1 into a shared util. - Attach-as-file vs inline-in-prompt decision (V1 attached, current V2 inlines — keeping inline for phase 1). Ordered work plan at the bottom: getIssueContent first, then PR, then internal-task (requires scoping). Acceptance criteria shows the expected prompt shape after the fixes land. * Update PR notes * lint * feat(desktop/v2): attachment framing + PR checkout hint + gap doc rewrite Prompt refinements from manual testing: - buildLaunchSpec {{attachments}} block now includes a short framing header: "Attached files — read them to understand the request." Cues the agent to actually use the files rather than treating them as passive metadata. Only appears when there are files. - githubPr contributor says "Branch `X` is checked out in this workspace — commits you make continue this PR." Confirms to the agent that the worktree is on the PR's branch, so it shouldn't create a new branch or open a new PR. - V2_LAUNCH_CONTEXT_GAPS.md rewritten with locked design decisions: bodies inline in prompt (no file writes for linked context), no truncation, no sanitization, PR checkout is true. Work plan: host-service getIssueContent → getPullRequestContent → task body API → swap stubs. Target prompt shape included. 54 tests green; 2 snapshots updated for new PR format. * feat(desktop/context): explicit kind labels in contributor headers Agents shouldn't guess whether a section is a task, issue, or PR from context clues. Each contributor now prefixes its heading with the kind: - `# GitHub Issue superset-sh#123 — Auth middleware stores tokens in plaintext` - `# Task TASK-42 — Refactor auth middleware` - `# PR superset-sh#200 — Rewrite auth middleware` PR phrasing also clarified: "This PR is checked out in this workspace on branch `fix/auth-encryption`. Commits you make here will be added to this PR." 54 tests green; 2 snapshots updated. * feat(desktop/v2): fetch issue + PR bodies via host-service, task via cloud API The launch prompt now includes full bodies for linked GitHub issues, PRs, and internal tasks instead of title-only stubs. Host-service (packages/host-service): - getGitHubPullRequestContent: new procedure wrapping octokit.pulls.get. Returns body, branch, baseBranch, author, isDraft, timestamps. (getGitHubIssueContent already existed.) Client (apps/desktop pending page): - buildForkAgentLaunch accepts an optional hostServiceClient. When provided, the issue + PR resolvers call getGitHubIssueContent / getGitHubPullRequestContent for full bodies. Falls back to pending-row title-only if the call fails (non-fatal). - Task resolver calls apiTrpcClient.task.byId (Superset cloud API, same source as the task view) for description. Falls back to title-only on failure. - dispatchForkLaunch threads the host-service client through. Contributors (already landed earlier this session): - GitHub issue header: `# GitHub Issue #N — Title` - PR header: `# PR #N — Title` + "This PR is checked out in this workspace on branch `X`. Commits you make here will be added to this PR." - Task header: `# Task ID — Title` - Attachments block: framing header cueing the agent to read the files. 77 tests green. Typecheck clean. * chore(desktop): fix biome warning + stale doc comment in fork launch - internalTask.test.ts: replace `TASK.description!` non-null assertion with `if (TASK.description)` guard (biome lint/style/noNonNullAssertion). - buildForkAgentLaunch.ts: update stale docstring that claimed bodies aren't fetched yet — they are, via host-service and the cloud task API. 77 tests green, biome clean, typecheck clean. * fix(host-service): shell out to gh CLI for issue/PR content (V1 parity) host-service's octokit path needs a GitHub token from providers.credentials.getToken("github.com") — which most users don't have set up (requires GITHUB_TOKEN env or git credential helper config for github.com). Result: getGitHubIssueContent / getGitHubPullRequestContent silently 500'd, buildForkAgentLaunch fell back to title-only, and the agent received empty bodies for linked issues/PRs. V1's projects.getIssueContent shells out to `gh issue view` via the user's `gh auth login` — that works out of the box. Port the same approach: - New packages/host-service/src/trpc/router/workspace-creation/utils/exec-gh.ts — promisified execFile("gh", ...) with user shell env so PATH resolves on macOS GUI contexts. - getGitHubIssueContent now calls `gh issue view <n> --repo owner/name --json number,title,body,url,state,author,createdAt,updatedAt`. - getGitHubPullRequestContent calls `gh pr view <n> --repo owner/name --json number,title,body,url,state,author,headRefName,baseRefName,isDraft,...`. - Zod-validate the JSON output before returning. - Normalize state to lowercase (gh returns "OPEN"/"CLOSED" uppercase). Drops the Octokit dependency on these two procedures. Other host-service paths that still use ctx.github() unchanged. * fix typecheck * clean up
…set-sh#3511) OPEN_IN_APP was registered both in OpenInMenuButton and in the v1 workspace page, so pressing Cmd+O ran two separate openInApp mutations (one per component's own useMutation instance) while clicking the button only ran one. Remove the page-level registration so the shortcut goes through the same handler as the click.
…ch (superset-sh#3509) * chore(deps): upgrade tanstack/db + electric, drop durable-streams patch - @electric-sql/client 1.5.13 → 1.5.14 (api, desktop, mobile) - @tanstack/db 0.5.33 → 0.6.4 (desktop, mobile) - @tanstack/electric-db-collection 0.2.41 → 0.3.2 (desktop, mobile) - @tanstack/react-db 0.1.77 → 0.1.82 (desktop, mobile) - @durable-streams/client ^0.2.1 → ^0.2.3 (api, desktop) - Remove @durable-streams/state@0.2.1 patch — state package isn't imported anywhere; the patch only stripped console.logs Type fixes for @tanstack/db 0.6 row virtual props ($synced/$origin/$key/$collectionId): cast pending rows back to PendingWorkspaceRow and use SelectTaskStatus directly in handleStatusChange so discriminated-union narrowing and callback variance keep working. * chore(deps): drop 7-day release-age policy, bump to latest patches - Remove `minimumReleaseAge` from bunfig.toml - @electric-sql/client 1.5.14 → 1.5.15 (api, desktop, mobile) - @tanstack/db 0.6.4 → 0.6.5 (desktop, mobile) - @tanstack/electric-db-collection 0.3.2 → 0.3.3 (desktop, mobile) - @tanstack/react-db 0.1.82 → 0.1.83 (desktop, mobile)
…superset-sh#3526) * docs(desktop): add v2 file editor audit + implementation plans * feat(desktop): v2 file editor foundation — shared document store + registry + CodeView * feat(desktop): v2 file editor state machine + chrome — pendingSave/saveError/conflict/orphaned/hasExternalChange + banners + conflict dialog * feat(desktop): v2 file editor views + toggle — MarkdownPreviewView, ImageView, BinaryWarningView, FileViewToggle * feat(desktop): v2 file editor fs:events — orphan detection, rename tracking, external-change reload * feat(desktop): v2 file editor close-pane save guard — wire Save action via non-hook fileDocumentStore.getDocument * chore(desktop): remove superseded v2 file editor code — old renderers + host-service useFileDocument * feat(desktop): move v2 file pane view toggle into the tab header via renderHeaderExtras Also inline FileIcon inside renderTitle so the file icon keeps rendering — the default pane header uses `titleContent ?? (icon + title)`, and upstream 3dd1de2 introduced the custom renderTitle for italic name + dirty dot without including the icon, suppressing it. * feat(desktop): v2 file editor visual polish — Lucide fold chevrons/placeholder, contour selection, palette-native highlight - Fold markers now render as Lucide chevron SVGs via foldGutter's markerDOM, aligned per-line-box regardless of font metrics. Hidden at rest, fades in on gutter hover. - Fold placeholder (collapsed block) renders as a Lucide MoreHorizontal button via codeFolding's placeholderDOM, with rounded corners + subtle hover background. - Custom selection layer (contourSelectionLayer): per-line RectangleMarkers snug to each line's actual text width, extending 4px past the last character, with a half-line-height stub for empty middle lines. Consecutive rects abut exactly. CM's default .cm-selectionBackground hidden — our layer is the only painter. - Selection background and active-line highlight share one palette-derived token (foreground at ~3% alpha), so they read as the same visual weight. Active-line hidden while a selection is active (selectionClassTogglePlugin toggles .cm-hasSelection on the editor root). - Dropped the gutter/content separator border. Line numbers get more left padding, less right padding now that there's no vertical rule. - @replit/codemirror-css-color-picker adds inline swatches on CSS color literals. - Exported withAlpha from shared/themes so editor theme can derive low-alpha variants without hardcoded rgba. * refactor(desktop): organize v2 CodeEditor into folder-per-module + dedupe view resolution Per AGENTS.md "one folder per module with barrel index.ts": - Extract inline helpers from CodeEditor.tsx into sibling extension folders: extensions/foldChevron, foldPlaceholder, contourSelectionLayer, selectionClassTogglePlugin. Hoists TRAILING_PAD / EMPTY_LINE_WIDTH_RATIO to module scope alongside their layer. - Move loose CodeMirror utility modules into their own folders: CodeEditorAdapter/, createCodeMirrorTheme/, loadLanguageSupport/ (with streamLanguages co-located as its only consumer), syntax-highlighting/. - Add registry/resolveActivePaneView/ — shared helper consumed by FilePane and FilePaneHeaderExtras to compute views + active view identically. Kills ~12 lines of duplication and removes one class of desync bugs. CodeEditor.tsx drops from 425 → 256 lines, focused purely on the React component. * refactor(desktop): move resolveActivePaneView under registry/utils/ AGENTS.md groups shared utility functions under utils/; relocate so the registry matches the convention. * feat(desktop): v2 file editor stability pass — rename tracking, save guards, conflict UX - Store passes trpcClient per-entry at acquire time so there's no global init race on hard reload; removes activeTrpcClient + requireClient. - Rename-safe acquire/release: iterate entries via snapshot to avoid mid-iteration map-insert loops; detect atomic-save renames (target matches open entry) and treat as content update; reuse handle when the entry was already migrated to the new path to avoid refcount leaks. - Preview-pane retarget: useSharedFileDocument now swaps handles on workspaceId/absolutePath prop changes via setState-during-render so the view never shows a stale file. - FilePane auto-follows renames by reconciling data.filePath from document.absolutePath, and auto-pins on first dirty transition. - Dirty state no longer mirrored into pane data; the tab title reads document.dirty live via a small FilePaneTabTitle component, which eliminates the store-cascade on first keystroke. Drop hasChanges from FilePaneData, and switch onBeforeClose / onBeforeCloseTab to read getDocument(ws, path)?.dirty. - Stable entry.id exposed on SharedFileDocument; CodeEditor keys on it so rename doesn't remount the editor and undo history is preserved. - Rename no longer flags hasExternalChange — a path change isn't a content change; disk conflicts continue to surface at save time. - Replace ExternalChangeBar + MultiFileDiff ConflictDialog with a VS Code-style alert (Save / Don't Save / Cancel) fired from FilePane. - CLOSE_TERMINAL usages in v2 swapped to CLOSE_PANE; the hotkey handler now runs the pane registry's onBeforeClose before closing. - Copy Path switches from electronTrpc to navigator.clipboard so it works when the host service is remote. - Active-line highlight bumped and switched to accent token; Cmd+Shift+/ vacated for the editor's built-in comment toggle on Cmd+/. * fix(desktop): v2 "Don't Save" now discards the dirty buffer Dirty document entries never dispose (refCount <= 0 && !dirty rule), so choosing "Don't Save" on close left the entry floating in the store. Reopening the same file reattached to that stale dirty buffer, which looked like dirty state bleeding between files. Both close prompts now reload the buffer to disk before resolving, and the multi-file tab close's "Save All" actually saves each dirty pane via doc.save() instead of the previous TODO no-op. * feat(desktop): v2 image + binary + large-file handling - Images load via binary encoding (readAsBinary = isImageFile) so PNGs and friends don't go through a corrupting utf-8 decode path. - ImageView renders on a 1:1 checkerboard underlay (10% foreground mix) so transparency reads correctly against either theme. - Non-image binaries stay as utf-8 text so "Open Anyway" can actually render them in the code view. The isBinary flag is still used for view resolution. - binaryWarningView demoted from exclusive → default priority, and codeView no longer matches binary files, so an image's view toggle shows [Image] only rather than [Binary, Image], and unknown binaries default to the BinaryWarning with code as a toggle fallback. - Default load cap bumped 2 MB → 10 MB. too-large now offers an "Open anyway" button via a new document.loadUnlimited() method. * feat(desktop): make v2 markdown preview read-only Matches VS Code's model — preview is a view of the source, not an editor. Avoids TipTap's markdown round-trip drift (parse → ProseMirror → serialize) that would spuriously mark files dirty and reformat on save. Edits continue to happen in the code view; preview re-renders from the shared document. * fix(desktop): address PR feedback from coderabbit review - save(): preserve live buffer when keystrokes arrive during an in-flight write — only bump revision/savedContentText on success. - loadEntry: distinguish ENOENT (not-found) from transient/permission errors; new { kind: "error" } content state + load-failed ErrorState with a Retry button. - resetForLoad helper shared by reload() and loadUnlimited() so both clear conflict/hasExternalChange/saveError consistently. - useCopyToClipboard: navigator.clipboard primary + offscreen-textarea + execCommand fallback (matches VS Code's browserClipboardService); preserves caller's selection range. - PathActionsMenuItems uses toast.promise for copy feedback. - CLOSE_PANE hotkey guarded with isClosingPaneRef against re-entrant prompts when Cmd+W is pressed rapidly. - ImageView switches from btoa(base64) data URLs to URL.createObjectURL(Blob) with cleanup; ~3× lower peak memory. - CodeEditorAdapter cut/paste async callbacks no-op after dispose. - MarkdownPreviewView drops the now-dead onSave prop. - Active-line and selection backgrounds share a theme.ui.accent @ 0.5 alpha so the selection is actually visible against the theme. - setup/steps.sh: Electric container now uses --restart on-failure:5 so a container stuck on a Neon session orphan doesn't loop indefinitely without the setup-script cleanup path running again.
…t-sh#3525) * docs(desktop): plan for v2 PR checkout via widened checkout procedure Design doc for wiring up linkedPR → worktree materialization in the v2 new workspace modal. Extends workspaceCreation.checkout with an optional `pr` field (shells to `gh pr checkout`) rather than adding a new tRPC procedure; client keeps a distinct `pr-checkout` pending intent for progress labels and payload construction. * docs(desktop): refine v2 PR checkout plan after walkthrough - Drop attach-time PR fetch; reuse existing getGitHubPullRequestContent call at pending-page time (zero net new fetches) - LinkedPR + pending-row schema stay narrow; PR content fetched on demand - Share branch.<name>.base config write across branch + PR paths via composer.baseBranch; fixes current gap where only create records base - Confirm forkOwner/headRefName naming over gh default (collision safety for our workspace-manager use case) * feat(host-service): PR-checkout mode in workspaceCreation.checkout Widen the checkout procedure with an optional `pr` field (structured PR metadata) so the modal's linkedPR can materialize the PR branch into a worktree via `gh pr checkout`. Exactly one of `branch` or `pr` enforced at the zod layer. - `getGitHubPullRequestContent` surfaces `headRepositoryOwner` and `isCrossRepository` (already returned by `gh pr view`, now mapped) - `derivePrLocalBranchName` pure helper: `<forkOwner>/<headRefName>` for cross-repo PRs, head ref as-is for same-repo - New `finishCheckout` local helper: `branch.<name>.base` config write + cloud register + rollback + local insert + setup terminal. Called from both the new PR path and the existing branch path — regular checkouts now also record their base (previously a gap where only `create` did) - PR path: detached worktree → `gh pr checkout --branch <derived> --force` → push.autoSetupRemote; returns `alreadyExists: true` on idempotent re-checkout of an existing workspace's branch - `composer.baseBranch` added; client fills from `pr.baseRefName` (PR mode) or picker selection (branch mode) - Warning surfaced when PR state is not "open" - `execGh` accepts cwd/timeout options; falls back to raw stdout when output isn't JSON (for `gh pr checkout`, which doesn't return JSON) * feat(desktop): wire pr-checkout intent through pending page + agent launch Modal routes to `intent: "pr-checkout"` whenever a linkedPR is attached (replaces the old fork-with-PR behavior that never checked out the PR's branch). The pending page fetches `getGitHubPullRequestContent` once, feeds the result into both the `checkout` mutation's pr payload and the agent-launch resolver — zero net new fetches vs the previous flow, which fetched the same data later for the prompt body. - pendingWorkspaceSchema.intent: "pr-checkout" added - useSubmitWorkspace: selects intent + placeholder name/branch from linkedPR when present - useCheckoutDashboardWorkspace: CheckoutWorkspaceInput widened with optional pr, composer.baseBranch - buildIntentPayload: buildPrCheckoutPayload pure builder + unit tests; buildCheckoutPayload plumbs composer.baseBranch - page.tsx useFireIntent: new pr-checkout case — imperative getGitHubPullRequestContent → buildPrCheckoutPayload → checkout.mutate → resolvedPr passed to dispatchForkLaunch so the agent-launch resolver skips a re-fetch - buildForkAgentLaunch: accepts resolvedPr; fetchPullRequest resolver returns it directly when URL matches - dispatchForkLaunch: threads resolvedPr through * fix(host-service): warn user when pr-checkout clobbers existing local branch Before running \`gh pr checkout --force\`, probe for a pre-existing local branch with the derived name via \`git show-ref --verify\`. If found, surface a warning pointing at \`git reflog\` for recovery — the user's point-and-click flow still completes, but they're informed and can recover any unpushed commits that got reset. Addresses PR review feedback (coderabbit/greptile/cubic-dev-ai): the idempotency check rules out Superset-managed workspaces but not stray branches created by a user's prior manual \`gh pr checkout\` in the primary working tree. Blocking would force CLI-level recovery, which is poor UX for a modal-driven flow; warning + reflog recovery is the right balance. * fix(host-service): harden pr-checkout against whitespace refs + deleted forks Two remaining PR review items: 1. `derivePrLocalBranchName` now trims `headRefName` before the empty check — previously a whitespace-only input (e.g. " ") slipped past the guard and produced a garbage branch name like "owner/ ". 2. `PrSchema.headRepositoryOwner` is now nullable — `gh pr view` returns null when the PR's head fork repository has been deleted, which previously crashed the zod parse. The getGitHubPullRequestContent return mapping uses `?.login ?? null`; buildPrCheckoutPayload fails fast on cross-repo PRs with null owner with a clear user-facing message ("head fork repository has been deleted") rather than letting the opaque server error bubble up. Same-repo PRs pass through an empty owner string (unused by the derivation).
…ce menu (superset-sh#3537) * feat(desktop): promote "Create Section Below" to top-level on workspace menu The workspace context menu previously hid section creation inside the "Move to Section" submenu, and the new section always appended to the end of the project — making "create" and "move" feel like the same action. Promote section creation to a top-level item, inserting an empty section directly below the right-clicked workspace so it matches the label, and leave "Move to Section" as a list of existing sections (hidden entirely when none exist). * refactor(desktop): separate "Create Section Below" from "Move to Section" with divider
… reveal (superset-sh#3512) * feat(desktop): v2 terminal honors terminalLinkBehavior setting When the user's "Terminal file links" setting is "file-viewer", Cmd/Ctrl-clicking a file path in a v2 workspace terminal now opens the file in an in-app FilePane instead of the external editor — matching the v1 behavior the setting already controls. Directories and the "external-editor" setting continue to fall through to the existing openFileInEditor path. * feat(desktop): modifier-keyed v2 terminal file links + folder sidebar reveal Replaces the settings-based branch with a modifier-key pattern: - Cmd/Ctrl-click a file path → opens in an in-app FilePane. - Cmd/Ctrl+Shift-click a file or directory → opens in the external editor (with an upfront toast for remote workspaces, same pattern as FilesTab's Open in editor guard). - Cmd/Ctrl-click a directory path → force-opens the sidebar, reveals the folder in the file tree (ancestors expand, row scrolls into view and highlights). Implementation reuses the existing selectedFilePath → fileTree.reveal machinery in FilesTab by promoting selectedFilePath from a pane-store derivation to a useState, synced from the active file pane via useEffect. Folder focus is just a direct setSelectedFilePath — the existing sidebar code path handles reveal + scroll + highlight without changes. Folder paths now also flow through getParentForCreation, so the "New File" toolbar button creates inside the focused folder. Three callbacks (onOpenFile / onRevealPath / onOpenExternal) are plumbed from page.tsx through usePaneRegistry to TerminalPane. The shift-modifier path goes through openExternal, which checks workspaceHost.hostMachineId !== machineId and toasts for remote workspaces instead of firing a mutation the remote won't satisfy. v1 code untouched; DB schema untouched; v2 settings UI untouched (terminalLinkBehavior still honored by v1). * refactor(desktop): drop callback refs in v2 TerminalPane, use effect deps directly * refactor(desktop): move v2 pane actions into a PaneActionsProvider context Removes the terminal-specific callback plumbing from usePaneRegistry (which should only care about how to render each pane kind) and moves onOpenFile / onRevealPath / onOpenExternal into a React context scoped to the workspace page. TerminalPane consumes via usePaneActions() instead of taking them as props. * refactor(desktop): drop PaneActionsProvider, pass actions through usePaneRegistry The context indirection wasn't worth it for a single consumer (TerminalPane). Passing the three callbacks through usePaneRegistry options is simpler and has no actual downside since usePaneRegistry is only called from one place anyway. * fix(desktop): v2 terminal folder reveal switches sidebar to Files tab Previously revealing a directory only toggled rightSidebarOpen + setSelectedFilePath, but the sidebar would stay on whichever tab the user had last (e.g., Changes/Review), leaving FilesTab unmounted so the reveal effect never fired. Update the same v2WorkspaceLocalState transaction to also force sidebarState.activeTab back to "files". * fix(desktop): auto-expand revealed directory + extract useOpenInExternalEditor - useFileTree.reveal now also expands the target itself when it's a directory, using stateRef so there's no staleness concern. All reveal call sites benefit. - Extract useOpenInExternalEditor hook (remote check + toast + mutate) so TerminalPane can consume it directly instead of through a callback. Drops one prop from usePaneRegistry and removes the local workspaceHost liveQuery from page.tsx. FilesTab's handleOpenInEditor could migrate to the same hook in a follow-up to dedupe the pattern. * feat(desktop): v2 terminal URL links open in internal browser by default Cmd/Ctrl-click a URL in a v2 terminal now opens a BrowserPane in the current tab. Cmd/Ctrl+Shift-click still opens in the external browser. Widens TerminalLinkHandlers.onUrlClick to receive the MouseEvent (v1's helper just threads it through unused — behavior unchanged). * feat(desktop): v2 terminal shows hover tooltip describing cmd-click action Adds onLinkHover/onLinkLeave callbacks to TerminalLinkHandlers, wired through LinkDetectorAdapter, UrlLinkProvider, and WordLinkDetector so every detected link participates. In v2 TerminalPane, a new LinkHoverTooltip component tracks hover + live modifier state (global keydown/keyup listeners scoped to hover duration) and portals a positioned tooltip to document.body when meta/ctrl is held. Content flips on shift: - File: Open in editor | shift: Open externally - Folder: Reveal in sidebar | shift: Open externally - URL: Open in browser | shift: Open in external browser v1's helpers.ts doesn't opt into the new callbacks, so v1 hover behavior is unchanged. * refactor(desktop): match tooltip styling, surface configured editor name - Tooltip now uses the same bg-foreground/text-background/rounded-md/px-3/py-1.5/text-xs tokens as the project's TooltipContent, so it visually matches the rest of the app (was a custom border/popover style before). - Shift-modifier tooltip text now says "Open in Cursor" / "Open in VS Code" / etc. based on the user's configured defaultEditor setting, resolved via electronTrpcClient.settings.getDefaultEditor + getAppOption display label. Falls back to "Open externally" if no editor is configured. * refactor(desktop): split LinkHoverTooltip hook/component, tighten modifier listener - Move useLinkHoverState into its own hooks/ folder (was exported alongside the component in violation of the one-component-per-file rule). - Effect now re-subscribes on hover start/end only (deps: hovering boolean), not on every modifier change. - Filter to Meta/Control/Shift/Alt key events so typing a letter while hovering doesn't churn state. - Skip setState when modifier/shift values didn't actually change, avoiding identity-change re-renders on repeat keydowns. - Extract tooltip offset constant. * fix(desktop): block external open while host data loads, surface editor-query failures - useOpenInExternalEditor now treats an unloaded workspaceHost as non-local (workspaceHost?.hostMachineId !== machineId) so Cmd+Shift-click doesn't fire the mutation against a potentially remote workspace before locality is confirmed. - LinkHoverTooltip's getDefaultEditor catch now console.warn's the error before falling back to null, so settings RPC failures stay observable.
…uperset-sh#3517) * remove 7 day rule * Upgrade mastra * upgrade ai * Ad mastra * refactor(desktop): remove dead provider-diagnostics plumbing The provider-diagnostics store was fed by callSmallModel's per-attempt reporting, which was removed when small-model tasks moved to direct AI-SDK + mastracode's AuthStorage. Nothing writes to the issue map anymore, so the clearIssue mutation, getStatuses query, and diagnosticStatus plumbing in ModelsSettings were all no-ops. Settings still surfaces "Session expired / Reconnect" via auth-status alone. ProviderIssue type collapsed from 8 codes to just "expired" to match. * fix(auth): auto-refresh expired Anthropic OAuth tokens Anthropic credentials were read via authStorage.get() everywhere, so mastracode's built-in refresh flow never ran. Once the 1-hour access token expired, status flipped to "Reconnect" and users had to do a full PKCE re-auth, even though a valid refresh token was already stored. Resolvers now call authStorage.getApiKey() for oauth creds on expiry, which triggers refreshToken() and persists the refreshed credential. getAnthropicAuthStatus does the same before declaring issue: "expired". Mirrors the pattern already used for OpenAI small-model auth. * review: address PR feedback from cubic + coderabbit + greptile - host-service ai-branch-name: run trailing-trim after slice so a 100-char truncation can't re-introduce a bare "." or "-" that git rejects as an invalid ref (coderabbit / cubic #2, superset-sh#7). - host-service workspace-creation.generateBranchName: reuse the existing listBranchNames helper instead of the inline git walk, which classified off the short refname and could conflate a local "origin/foo" with refs/remotes/origin/foo (coderabbit superset-sh#3). - packages/chat shared/small-model: drop the unused hasSmallModelCredentials export; only a test mock consumed it (greptile superset-sh#4). - resolveAnthropicCredential: on refresh failure, return null instead of kind:"oauth" with a stale expiresAt so callers fall back cleanly (cubic superset-sh#8). - chat-service.getAnthropicAuthStatus: log context when refresh throws instead of silently swallowing (cubic superset-sh#9). * fix(chat): read auth.json directly instead of importing mastracode Importing createAuthStorage from mastracode loads the entire CLI tree (fastembed → onnxruntime-node's 208 MB native binary) via eager top-level requires in mastracode's CJS entry. This crashed electron-vite bundling and bloated the get-small-model chunk. getSmallModel now reads mastracode's auth.json file directly using the same path resolution logic (~/Library/Application Support/mastracode/ on macOS). Zero mastracode import, zero bundle impact. The chunk stays at 1.2 MB (just @ai-sdk/anthropic + @ai-sdk/openai). Production build verified: compile:app succeeds, Electron main process boots with no onnxruntime error. * docs(desktop): add manual testing plan for PR superset-sh#3517 * fix api key storage slot * fix(auth): store API keys in dedicated slot so OAuth doesn't clobber them setApiKeyForProvider and setStoredAnthropicApiKeyFromEnvVariables now use authStorage.setStoredApiKey() (writes to "apikey:<provider>") instead of authStorage.set() (writes to the main "<provider>" slot shared with OAuth). This way connecting/disconnecting OAuth doesn't overwrite or delete a stored API key. resolveAuthMethodForProvider falls back to hasStoredApiKey() after checking the main slot, so status correctly reports authenticated when only an API key is stored. * fix(auth): backup/restore API keys across OAuth connect/disconnect mastracode's resolveModel only reads API keys from the main authStorage slot (authStorage.get("anthropic")). OAuth login overwrites this slot, and disconnect removes it — losing any previously saved API key. Fix: backup the API key to the dedicated apikey: slot before OAuth connect, restore it after disconnect. setApiKeyForProvider now writes to both slots (main for resolveModel compatibility, apikey: for backup). resolveAuthMethodForProvider checks both. Applies to both Anthropic and OpenAI providers. * chore: add upstream PR reference to auth workaround Point to mastra-ai/mastra#15483 so the backup/restore code can be removed once upstream lands and we bump mastracode. * refactor(desktop): derive settings provider action from status Replace the cascade of if/else + canDisconnect flag with a single getProviderAction(status) → connect | reconnect | logout | null. Fixes "Active" badge + "Connect" button showing simultaneously when authenticated via API key. * fix(desktop): always show Logout when provider is active Active providers now always show a Logout button. Clears OAuth or API key depending on authMethod — no more "Active" badge with no way to disconnect. * fix(desktop): simplify OpenAI OAuth dialog + auto-open browser Match Anthropic dialog's layout: remove the raw OAuth URL display and "Tip" block, auto-open the browser on OAuth start. Change "Back" to "Cancel" for consistency. * refactor(desktop): unify OAuth dialogs into shared OAuthDialog Extract shared OAuthDialog component with provider config object. AnthropicOAuthDialog and OpenAIOAuthDialog become thin wrappers that pass provider-specific labels and options. * fix(desktop): show 'Copied!' feedback on Copy URL button * refactor(desktop): merge provider account + API key into single card Each provider section now renders AccountCard + ConfigRow inside one rounded card with a divider, instead of two separate cards. Removes the standalone "API Keys" collapsible section. * refactor(desktop): compact OAuth row in provider settings card OAuth row is now a single inline row (label + status + action) instead of a stacked AccountCard. Both providers share the same 2-row card layout: OAuth row + API key row with divider. * fix(desktop): contextual buttons in provider settings Connect is now primary (filled). Save only shows when there's input. Clear only shows when a key is saved. Removes visual noise from empty-state provider cards. * ui(desktop): add provider icons to settings section headers * ui(desktop): show 'Not connected' badge instead of subtitle for disconnected providers * ui: remove redundant disconnected subtitle * ui: remove subtitle text from OAuth rows * chore: remove dead AccountCard + getProviderSubtitle * docs: update test plan to match current UI * chore: move shipped plans to done/ --------- Co-authored-by: AviPeltz <aj.peltz@gmail.com>
…rset-sh#3542) * fix(desktop): wire drag-and-drop for files in v2 terminal panes Drops from Finder or the file tree now focus the terminal, shell-escape the path, and paste it at the cursor. A dashed overlay indicates an active drag. * fix(desktop): handle multi-file terminal drops, align priority with v1 Check dataTransfer.files before text/plain (native Finder drags win over internal drags) and escape every dropped path, not just the first.
…spaces (superset-sh#3544) Ungrouped workspaces that render after a section header are already visually grouped with that section (shared accent color, collapse together) and get committed into it on next drag. The count, however, was reading section.workspaces which only included workspaces whose DB sectionId already matched — so a freshly created section showed "(0)" while the user sees N workspaces beneath it. Reparent those workspaces into section.workspaces in the data layer so the count matches the visual grouping and DnD commit behavior.
…rset-sh#3545) When panes mount during new-workspace / preset flows, attachToContainer's fit() can run before flex layout resolves, leaving the backend PTY spawned at stale defaults (shell prompt wraps wrong until a manual resize). Mirror v2's sendResize-on-open pattern: once createOrAttach succeeds, re-fit against the now-settled container and push the real dims.
superset-sh#3548) v1's createWorktree appended ^{commit} to the start point to prevent implicit upstream tracking. This fails with "fatal: invalid reference" when the ref isn't locally resolvable with that suffix (e.g. stale or missing remote-tracking ref, branches with slashes like feat/workstreams-view). Replace ^{commit} with --no-track, which has the same effect without fragile ref suffix manipulation. Matches v2's host-service approach. Closes superset-sh#3448
…erset-sh#3549) * fix(desktop): guard installUpdate against repeat clicks MacUpdater.quitAndInstall() registers a fresh native-updater `update-downloaded` listener each call when Squirrel.Mac hasn't finished staging. Repeat clicks on the update button stacked listeners, then fanned out into parallel nativeUpdater.quitAndInstall() calls once Squirrel fired — racing to swap the binary and leaving the app on the old version. Matches the reporter's symptom (app quits, reopens on same version). Add an `isInstalling` guard + `status === READY` precondition so repeat clicks collapse to a single quitAndInstall, and clear the flag in the error handler so the user can retry if Squirrel surfaces an error instead of actually quitting. Closes superset-sh#3507 * test(desktop): make auto-updater tests portable + non-destructive reset Greptile flagged that setupAutoUpdater() short-circuits on non-mac/linux hosts, so the suite would silently fail on a Windows CI runner (handlers never register, guard never resets). Mock shared/constants to pin the platform. CodeRabbit flagged that the beforeEach reset emitted a non-network error, triggering the real ERROR path (which also clears the cached update). Use a network-shaped error so the handler maps back to IDLE without the extra side effect.
…licks (superset-sh#3552) * fix(desktop): refresh v2 terminal link tooltip editor label + nudge plain clicks - LinkHoverTooltip fetched the default editor in a mount-only useEffect, so changing the default editor in settings left the modifier-shift label ("Open in Cursor", etc.) stale until the terminal pane unmounted. Refetch on every hover-start instead. - Plain (no-modifier) clicks on a detected file path in the v2 terminal were silent, which made the modifier-key affordance undiscoverable. On a plain file-link click, show a transient tooltip at the click position ("Hold ⌘ to open · ⌘⇧ for external", or Ctrl/Ctrl+Shift off-mac). Capped at two shows per renderer session via a module-level counter, and suppressed while the modifier-hover tooltip is already visible. Uses framer-motion's AnimatePresence for fade in/out. * refactor(desktop): simpler v2 terminal link tooltip labels - "Open in editor" → "Open in pane" for the ⌘-click file case (native in-app file pane is what actually happens). - Shift variant always says "Open in external editor" instead of interpolating the configured editor name. openFileInEditor uses the global settings defaultEditor (non-editor apps like Finder can't be set there), so the interpolated name could disagree with a user's per-project preference — the generic label never lies. - Drops the getDefaultEditor fetch, defaultEditor state, and getAppOption/ getAppLabel plumbing that went with it. * refactor(desktop): URL ⌘-click tooltip says "Open in pane" for consistency
…rset-sh#3551) requestLocalNetworkAccess was defined in local-network-permission.ts but never called, so the Info.plist keys (NSLocalNetworkUsageDescription, NSBonjourServices) wired up in electron-builder never had a trigger to prompt the user. On macOS 15+ this causes outbound connections to local-network IPs from the app and its spawned child processes (node, python in the terminal) to be silently blocked, while system binaries like curl escape the same TCC attribution. Call it alongside requestAppleEventsAccess in app ready. Refs superset-sh#3474
…h#3553) Adds a Tasks nav entry (collapsed + expanded) alongside Workspaces, mirroring v1: paywall gate, last-used filter restoration, and active-route highlight.
…set-sh#3478) (superset-sh#3550) The v1 terminal host buffered all stdin writes — user keystrokes included — until the shell emitted OSC 133;A. When a user's `.zshrc` hook (e.g. fnm's `use-on-cd`) opened an interactive prompt during init, the marker never fired and typed y/N answers sat in the queue for the full 15s timeout, making the workspace look frozen. Pass writes straight through instead, keeping only the escape-sequence drop for stale DA/DSR replies from the renderer's xterm. Mirrors the v2 host-service behavior, which has always written user input directly.
…t-sh#3372) (superset-sh#3547) * fix(desktop): stop excessive lsof spawning from port scanner (superset-sh#3372) PortManager was spawning `lsof` every 2.5s via a module-level `setInterval` with no shutdown path, stacking hint-triggered scans on top, and wrapping each call in `sh -c` so timeouts left orphaned children that outlived the app. Three fixes: 1. Lifecycle: the interval now starts on first `registerSession` / `upsertDaemonSession` and stops on the last unregister. No sessions, no scans. 2. Concurrency: hint-triggered scans coalesce via a single debounced timer plus a `scanRequested` follow-up flag, so at most one scan is ever in flight. 3. No orphans: swapped `exec("sh -c 'lsof ...'")` for `execFile` and threaded an `AbortSignal` through so `stopPeriodicScan` can cancel any in-flight child instead of leaking it to `launchd`/`init`. Also narrowed `containsPortHint` by dropping two over-broad patterns (`/port\s+(\d+)/i`, `/:(\d{4,5})\s*$/`) that matched routine log noise and forced spurious scans. Includes 13 regression tests in `port-manager.test.ts`, 8 of which fail on `main` — they directly measure the three classes of bug. * update doc * fix(desktop): forward AbortSignal to Windows process-name lookups; add ready-on regex test Addresses PR superset-sh#3547 review feedback: - Forward `signal` from `getListeningPortsWindows` through to the `wmic` and `powershell` child lookups in `getProcessNameWindows`. Previously, on Windows with many unique PIDs, the per-PID process-name lookups were not covered by the abort and could run up to 5s after teardown. - Add a positive-case regression test for the `/ready on .../` hint regex (Vite-style dev server output) — the third retained pattern previously had no positive test. Skipping the proposed "snapshot signal at top of scanAllSessions" race fix: without the shell wrapper, OS process-group cleanup on Electron exit already handles child termination, so the narrow race between `stopPeriodicScan` and a read of `this.scanAbort?.signal` mid-scan can only waste one in-flight lsof call (~100ms) and cannot produce an orphan. Not worth the extra indirection. * fix(desktop): tighten runTolerant abort handling + exact coalesce assertion Addresses PR superset-sh#3547 review feedback: - runTolerant: rethrow on AbortError/ABORT_ERR/killed/signal so partial stdout from a mid-execution kill is never parsed as success. Only plain non-zero exits (e.g. lsof exit 1 when no PIDs match) are tolerated. The outer catch in getListeningPortsLsof turns any rethrown abort/timeout into an empty result — same upstream behavior as before, but explicit about intent. - port-manager.test.ts: tighten the coalescing assertion from toBeLessThanOrEqual(2) to toBe(2). The regression guarantee is "exactly one initial scan + one coalesced follow-up" — the loose version also passed if follow-ups were silently dropped.
…#3560) - drizzle-orm 0.45.1 → 0.45.2 (GHSA-gpj5-g38j-94v9 / CVE-2026-39356) - better-auth family 1.5.6 → 1.6.5 (GHSA-xr8f-h2gw-9xh6)
…perset-sh#3561) Disabled state alone didn't feel responsive — click registered without obvious acknowledgement. Adds a spinning icon alongside the existing "Installing..." label so users see the action was received.
… chat input (superset-sh#3520) * fix(desktop): prevent default on hotkeys to stop character leak into inputs * fix(desktop): make hotkey preventDefault opt-out-able
Rename TOGGLE_EXPAND_SIDEBAR to OPEN_DIFF_VIEWER (same binding). In v2, focus any existing diff pane or open one in a new tab, and flip the workspace sidebar to the Changes tab. V1 keeps its existing expand-sidebar behavior under the new ID.
…t-sh#3513) (superset-sh#3554) * fix(desktop): recover terminal from non-monospace font crash (superset-sh#3513) Setting the terminal font to a proportional family like "Inter" blanked the app on next launch — the bad value persisted in SQLite and xterm couldn't lay out cells on reload, leaving no way back into settings. - Sanitize the stored family on read: if the primary family isn't monospace (per canvas measurement), fall back to the default terminal font so a poisoned DB value can never blank the renderer. - Hide the "Other" group and custom free-form entry in the terminal font picker so new selections are restricted to monospace candidates. * fix(desktop): reject all-proportional generic terminal font stacks Follow-up on superset-sh#3554 review. sanitizeTerminalFontFamily previously passed any all-generic CSS value through untouched (e.g. "cursive", "sans-serif", "monospace, sans-serif") because parsePrimaryFontFamily returns null when no concrete family is present — same blank-window crash class as the "Inter" report. Refactor the sanitizer to inspect the full family list: when no concrete primary exists, only trust the value if every entry is a monospace generic; otherwise fall back to the default. Add regression tests. * refactor(desktop): append ", monospace" fallback + cleaner font preview - Always append "monospace" to the sanitized terminal font stack when it doesn't already end with one. Mirrors VS Code's behavior in src/vs/workbench/contrib/terminal/browser/terminalConfigurationService.ts so that if the configured primary isn't installed on this machine, the browser falls back to the OS monospace generic instead of a proportional default. - Swap the terminal font preview from a box-drawing layout (which rendered as broken in proportional fonts and used tofu glyphs) to a shell session that demonstrates column alignment naturally. - Drop a couple of narrating comments flagged in simplify review. * refactor(desktop): show Nerd Fonts in the editor picker too Nerd Fonts are monospace — the terminal-only gate was pre-existing special-casing and reviewers pointed out it hides a widely-used class of fonts from users picking an editor font. Drop the gate. * fix(desktop): validate the actual CSS primary font, not the first concrete entry Addresses the coderabbit review on b2e6a04. The sanitizer previously skipped leading generics when picking the primary to measure, so a value like `sans-serif, "JetBrains Mono"` passed validation because the later concrete entry was monospace — but CSS resolves the first generic (sans-serif) and the terminal still renders proportional. Switch to validating families[0] (the actual CSS primary): if it's a monospace generic, trust the stack; if it's a proportional generic, fall back; if it's concrete, canvas-measure it. Add regression tests.
…ts (superset-sh#3562) Memory leak and CPU spiral root-caused to `staleTime: 0, gcTime: 0` + 60fps polling: React Query can't dedupe or GC anything, and the render path churns allocations every 16ms. Restoring the React Query defaults (5min gcTime) fixes the leak. Server poll rate is independent of perceived stream smoothness — StreamingMessageText already reveals text client-side at 60fps from whatever buffer the server delivers. 4fps polling keeps that buffer fed with plenty of headroom. Also removes the `isRunning` invalidation effect — redundant when the query is polling. Builds on and supersedes superset-sh#3170 by @thepathmakerz, which diagnosed the same root cause. This version takes the subtractive path (-21 lines) instead of adaptive polling (+36). Closes superset-sh#3049
…superset-sh#3563) * Remove video section * Rband * More brand * Move pills * CTA * Color * Grid line * polish marketing hero, CTA, and testimonials - Hero title: both segments at weight 500; "AI Agents." uses lo-res-21-ot-serif with Pixelify Sans fallback. - Subtitle: "Orchestrate 100+ coding agents in parallel" (keep rest). - Remove vertical grid lines from <main>. - Move ProductDemo pills back under the mockup (no scroll transform). - CTA heading matches FAQ styling; copy now "Try Superset now.". - Add `role` field to Iven's testimonial (Engineer at Paraflow). - TypewriterText: optional per-segment `render` for custom glyph rendering. - Simplify DownloadButton classes (unify with buttonClasses). * revert hero 'AI Agents.' styling to main version * swap feature-demo background to paper-design Dithering shader Replaces the canvas-based Bayer dither with @paper-design/shaders-react's <Dithering> shader (shape="warp", type="4x4"), lazy-loaded via React.lazy. Rendered at opacity 30% with mix-blend-screen over each card's palette. * slow feature-demo dither shader from 0.3 to 0.15 * subtle hover on trusted-by logo tiles (brighter border + bg) * soften Download button: ghost-style over solid foreground * recolor Download button ghost style to brand orange * match header CTA to brand ghost style * push CTA button text to a more saturated orange * restore cursor:pointer on <button> (Tailwind v4 preflight default is default) * unify header CTA with DownloadButton * update Chris Laupama role to TS Lead at Webflow * match TrustedBy heading style to WallOfLove * shrink TrustedBy heading one step * use original compact size on TrustedBy heading, keep semibold * update Elias role to Founder at Cleanroom * update Chase role to Founding Engineer at Decoda Health * update Felipe role to Codex at OpenAI * drop unused Adobe Fonts kit scaffolding and lo-res-22 font-family * restore mobile horizontal scroll on ProductDemo pills
* feat(chat): render subagent activity inline as collapsible tool wrapper
Remove SubagentExecutionMessage from the bottom-pinned section so subagent
tool calls render inline within AssistantMessage via the existing
SubagentToolCall collapsible component, consistent with all other tool calls.
* fix(chat): add gradient fade-out and bottom padding to input footer
Removes top padding from ChatInputDropZone so content is flush with the
bottom of the scroll area, and adds a CSS pseudo-element gradient that
fades the conversation content into the background above the input bar.
* feat(ui): redesign ToolInput/ToolOutput/ToolHeader — compact, monospaced
- ToolInput/ToolOutput: replace CodeBlock with plain <pre>, remove p-4
padding, switch to bg-muted/30 backgrounds, rename labels to
"Input"/"Output", apply font-mono throughout
- ToolHeader: reduce px-2.5 → px-1, add rounded-b-md, swap chevron
direction on hover (right=collapsed, down=expanded), add open prop
- Tool: add font-mono to outer Collapsible wrapper
* fix(ui): stop scroll anchor when clicking tool call triggers
Expanding a tool call was causing the scroll container to jump. Detect
clicks on [data-tool-trigger] elements and call stopScroll() so the
stick-to-bottom behaviour doesn't fight the user interaction.
* feat(ui): add ToolCallRow and refactor tool components
Introduces a shared ToolCallRow component that encapsulates the common
collapsible row pattern: icon/chevron, ShimmerLabel title, muted
description, status slot, left-border content area, and an optional
headerExtra element for out-of-trigger action buttons.
Refactors BashTool, WebSearchTool, WebFetchTool, and FileDiffTool to use
ToolCallRow, removing duplicated hover/chevron/collapsible logic from each.
All tools now have consistent: font-mono, px-1 header padding, rounded-b-md
on hover, ml-2.5 border-l content alignment, and chevron-right/down hover
behaviour.
* feat(desktop): refactor tool call components to use shared ToolCallRow
Migrates GenericToolCall, SupersetToolCall, SubagentToolCall, and
ReadOnlyToolCall to use the new ToolCallRow component, eliminating
per-component chevron/hover/collapsible boilerplate.
Also updates ToolCallBlock dispatch: web_search tool variants without
parsed results now fall through to GenericToolCall with a globe icon
instead of WebSearchTool (which requires results to be expandable).
Renames "Subagent" label to "Agent" with the agent type as a muted
description.
* feat(chat): replace inline question UI with footer overlay
- Add QuestionInputOverlay component: numbered options, "Something else"
free-text row with pencil icon, Skip button, cross-fade to submit on type
- Refactor AskUserQuestionToolCall to plain ToolCallRow (no inline answer UI)
- Wire pendingQuestion/handleQuestionResponse/stopActiveResponse to footer
in both ChatPane variants instead of ChatMessageList
- Remove PendingQuestionMessage from ChatMessageList and clean up its props
* feat(ui): replace tool call header indicators with braille spinner and left-side icons
- Add BrailleSpinner component (braille chars, amber, matches sidenav style)
- Show spinner in icon slot while pending, red X on error, icon on complete
- Remove right-side status slot (no spinner, checkmark, or X on the right)
- Remove title shimmer animation
* feat(chat): hide Question tool row while active, show with description after answered/interrupted
- Thread isStreaming through ToolCallBlock to AskUserQuestionToolCall
- Return null only when isPending && still streaming (overlay is active)
- Show collapsed row with question text as description once answered or interrupted
* feat(chat): improve tool call UX and styling
- Align tool call icon with chat text using -mx-1 negative margin
- Remove overflow-hidden from MessageContent to avoid clipping
- Hide description in tool call header when expanded
- Show query field as plain text with Query/Response labels instead of raw JSON
- Add subtle focus-visible ring to collapsible trigger button
- Adjust content padding (pl-3 py-1) to align with heading text
- Use "Type your answer..." placeholder when question has no options
* fix(chat): keep answered question messages visible during active turn
Extract hasAnsweredQuestionToolCall to a shared utility and use it in
withoutActiveTurnAssistantHistory / getVisibleMessages so that assistant
messages containing an answered ask_user_question are kept visible in the
message list while a session is still running. Previously all assistant
messages from the active turn were hidden, causing questions and their
answers to disappear from chat history until the turn completed.
* fix(chat): refactor question tool call — answer bubble, skip badge, plain-string fallback
Replace the inline Q&A markdown block with a right-aligned answer bubble
(styled like a user message) shown after the question is answered, and a
"Question skipped" badge when the result carries no answers. Add a fallback
for backends that return a plain string result (result.text / result.answer)
rather than a structured answers map. Remove the isPending spinner from the
ToolCallRow since the overlay in the footer already indicates active state.
* fix(chat): overlay freezes in place on submit, skip sends answer, scroll on question changes
- Fire onRespond without awaiting so the overlay never shows a separate
"Waiting for response..." state. The overlay stays frozen (same size and
content) until pendingQuestion updates from the server, at which point
React remounts the component via key={pendingQuestion.questionId}.
- Highlight the chosen option and replace its number badge with a spinner;
show a spinner on the pencil icon when the answer came from the text input.
- Skip now sends "skip" as the answer instead of aborting the agent, so the
LLM can continue. The X button still stops the session.
- Fix isQuestionSubmitting hardcoded to false — now passes questionResponsePending.
- Add footerScrollTrigger so the chat scrolls to the bottom whenever the
question overlay appears, updates, or disappears.
* feat(chat): question overlay max-height with scrollable options and pinned header/footer
* fix(chat): stabilize useFocusPromptOnPane effect dependency
* feat(chat): show question tool call with status and collapsible answer
Rewrites AskUserQuestionToolCall to use the shared ToolCallRow component.
- Supports both ask_user (singular question/options) and ask_user_question
(array of questions) tool schemas
- Shows AWAITING RESPONSE / ANSWERED / CANCELLED inline status description
- Expands to reveal the question text + submitted answer when answered
- Extracts answer from result.content "User answered: <x>" format
- ToolCallRow now uses cursor-text when the row has no expandable content
* fix(chat): keep question tool calls visible during active assistant turn
getVisibleMessages() filtered out all assistant messages while a turn was
in progress. Added hasPendingQuestionToolCall() to also pass through any
assistant message that contains an unanswered question tool call, so the
"AWAITING RESPONSE" tool call row remains visible in the message list.
* feat(chat): optimistically hide question overlay on answer submit
Tracks the most recently answered question ID in ChatPaneInterface and
passes it to ChatUploadFooter so the overlay disappears immediately on
submit without waiting for the server round-trip. Also threads
pendingQuestion and answeredQuestionId down to ChatMessageList to suppress
the ThinkingMessage spinner while a question is awaiting a response.
* feat(chat): scroll to bottom on message send, question arrival, and answer
Adds a ScrollAnchor component inside the Conversation (StickToBottom)
context that handles three cases:
- isAwaitingAssistant becomes true: re-pins scroll so Thinking and the
streaming response are always visible after sending any message
- pendingQuestion.questionId changes: scrolls to bottom when a new
question arrives so the overlay doesn't cover streaming content
- answeredQuestionId changes (10ms delay): the overlay hide causes the
footer to shrink and the scroll container to grow; the library
interprets the resulting scrollTop clamp as "user scrolled up" via a
1ms setTimeout, so we run after it with a 10ms delay to restore the pin
* feat(chat): show cancelled status when question is aborted
- Question tool call now shows CANCELLED status in the header instead of
nothing when aborted (output-error state or Mastracode isError: true)
- Error result content is no longer mistaken for an answer, fixing the
ANSWERED status shown on revisit after an abort
- Expanded view shows the question text and an "Aborted by the user"
label with a red CircleX icon
- INTERRUPTED / Response stopped footer is suppressed when the
interruption was caused by an aborted question
* fix(chat): keep bottom-pinned scroll when expanding a tool call
When the chat is pinned to the bottom and the user opens a collapsible
tool call, skip stopScroll() so stick-to-bottom's resize handler
auto-scrolls to reveal the expanded content instead of hiding it behind
the prompt input.
* fix(chat): exclude scrollbar column from input footer gradient
* fix(chat): stop scroll jump when expanding any tool call
Remove the overly-broad "scroll to bottom if last trigger" heuristic that
used a DOM query to find the last [data-tool-trigger] in the container.
This was wrong — it would fire even when there were messages below the
tool call being expanded.
Now ConversationContent simply unpins from bottom on any tool trigger
click, preventing the resize handler from jumping the scroll position.
Nothing more.
* feat(chat): always use ask_user tool for questions in Superset
Two-pronged approach to ensure the LLM never asks questions as plain text
(which bypasses the question overlay) and always uses the ask_user tool:
1. AGENTS.md — adds a project-level override rule that loads into the
mastracode system prompt for any session in the Superset workspace.
2. host-service — writes a managed ~/.mastracode/AGENTS.md with the same
rule, applied globally to every workspace opened in the desktop app.
Uses a managed-by marker to avoid overwriting user-authored files.
Root cause: the default mastracode tool guidance says "Don't use this for
simple yes/no — just ask in your text response." These rules override that
by being appended to the system prompt after the base instructions.
* feat(chat): pending question drives workspace nav status and native notification
- When ask_user tool fires, emit PendingQuestion lifecycle event from the
harness subscriber (same pipeline as PermissionRequest/Start/Stop)
- useAgentHookListener maps PendingQuestion → "permission" pane status,
showing the orange dot in the workspace nav immediately, even when the
tab is not focused
- NotificationManager plays sound and shows "Awaiting Response" native
toast for PendingQuestion events with visibility suppression
- Cancel tooltip added to QuestionInputOverlay X button
- Cancelled question tool calls now show question text + "Aborted by the
user" immediately on stop, without requiring a page reload
- isInterrupted prop threaded through MessagePartsRenderer → ToolCallBlock
→ AskUserQuestionToolCall so pending questions show CANCELLED status
when the run is interrupted
- INTERRUPTED badge is always shown alongside cancelled question state
* fix(chat): clear orange dot on answer submit, focus prompt on dismiss, no green dot when tab is focused
- Clear pane status to idle immediately when user submits a question answer
- Focus prompt textarea when question overlay dismisses (rAF to let overlay unmount and browser focus settle)
- Fix Stop event idle/review determination: read URL from hash (not pathname, which is always the file path in hash-routed Electron app), and add focusedPaneIds as a reliable fallback so panes the user is actively interacting with don't receive a spurious green dot
- Remove MarkdownToggleContent in favour of always-on MessageResponse for subagent output
* feat(chat): render subagent task prompt with markdown via MessageResponse
Render the subagent task text through MessageResponse so markdown
formatting (lists, bold, code spans) is applied consistently with
the response text below it.
* fix(chat): scale down headings and fix list layout in subagent output
Headings were rendering at full browser size (text-2xl/3xl) inside the
compact xs subagent block. Override h1-h6 to text-sm/xs with tighter
margins, and remove the top margin on first-child paragraphs inside list
items to fix ordered list numbers appearing on a separate line.
* feat(chat): render read file tool output with syntax-highlighted code viewer
- ReadOnlyToolCall fetches file content directly via tRPC filesystem.readFile
(same path as the file pane) instead of parsing MCP tool output, eliminating
metadata artifacts like line-number prefixes and byte-count headers
- Uses shared detectLanguage() for syntax highlighting language detection
- Renders with CodeBlock (Shiki) with a filename + line-range header styled
like table headers (bg-muted/50), showLineNumbers, and colorize=false for
plain white text
- Adds colorize prop to CodeBlock to suppress syntax colors while keeping
line numbers at reduced opacity
- Passes workspaceId/workspaceCwd from ToolCallBlock to ReadOnlyToolCall
- Fixes withoutActiveTurnAssistantHistory to preserve completed prior-phase
assistant messages (e.g. read-file before a question answer) by keeping
messages that have a stopReason and a different id from currentMessage,
preventing tool calls from disappearing after answering a question
* feat(chat): improve tool call error and task_write UX
- ToolCallRow: replace XIcon with "ERROR" label + XCircleIcon in description slot on error
- SupersetToolCall: add subtitle prop, render output content via MessageResponse instead of raw JSON
- TaskWriteToolCall: new component for task_write — "Update Tasks" title with ListTodoIcon and semantic description (task count + status breakdown)
* fix(chat): add vertical padding to read file tool content area
* feat(chat): add LspInspectToolCall with ActivityIcon and file subtitle
* fix(chat): handle mastra_workspace_lsp_inspect tool name alias
* fix(chat): use FileSearchIcon for LSP Inspect tool call
* feat(chat): show input/output content in LSP Inspect tool call
* fix(chat): use SearchCheckIcon for LSP Inspect tool call
* fix(chat): extract TOOL_CALL_MD_CLASSNAME for global compact markdown in tool calls
Adds inline code (text-xs) fix alongside existing heading overrides.
SubagentToolCall and SupersetToolCall now share the same constant so
future patches only need to happen in one place.
* feat(chat): improve subagent tool call display and share read-file component
- Filter empty messages (step-start/source-only) from chat history
- Add expandable content with subtitles to subagent inner tool calls (Read, List Files, Search, Write, Edit, Web)
- Extract shared ReadFileTool component to packages/ui for reuse across main and subagent tool calls
- Subagent read tool now shows styled CodeBlock with syntax highlighting, matching main agent display
- Thread workspaceId/workspaceCwd/onOpenFileInPane through SubagentToolCall → SubagentInnerToolCall so subagent read calls show the open-in-pane button
* fix(chat): forward workspace props to ReadOnlyToolCall in MessagePartsRenderer
workspaceId and workspaceCwd were available in MessagePartsRenderer but not
forwarded to ReadOnlyToolCall, silently disabling the disk-read feature for
read_file tool calls rendered via that path.
* fix(chat): remove dead code from MessageList
- Remove interruptedByAbortedQuestion which was computed but never
referenced in JSX or logic
- Move hasRenderableParts to after imports (was inserted between them)
- Remove unused getToolName, normalizeToolName, ToolPart imports
- Add comment explaining the runtime-only "error" part type cast
* fix(chat): fix type safety, path normalization, and memoize in SubagentInnerToolCall
- Replace as never with as BundledLanguage for language prop type safety
- Replace naive path concatenation with normalizeWorkspaceFilePath to handle
./, ../, file:// and workspace boundary validation (matches ReadOnlyToolCall)
- Rename shadowed normalized variable to resolvedPath in openInPane closure
- Memoize parseReadFileResult call to avoid re-parsing on every render
* fix(chat): add stale time and loading state to ReadOnlyToolCall file query
- Add staleTime: Infinity so completed read-file tool calls don't refetch
on remount (prevents IPC burst when scrolling long conversations)
- Show a spinner row while the disk read is in flight instead of flashing
the raw ToolInput/ToolOutput view
* feat(chat): replace text input with Tiptap editor for slash commands and file mentions
- Add TiptapPromptEditor with ProseMirror-based rich text input
- Slash command chips (/command) as inline atom nodes, insertable anywhere in message
- File mention chips (@path) anchored to cursor position via virtual float
- SlashCommandMenu width matches prompt input via --radix-popover-trigger-width
- Selecting a command inserts a chip node instead of immediately submitting
- Popover closes on editor blur, reopens on focus
- Tab no longer auto-selects commands (only Enter selects)
- serializeEditorToText serializes chip nodes to /name and @path for submission
* feat(chat): add skill preload — /command chips trigger skill tool calls before LLM
- Add SkillToolCall component (ZapIcon, Skill(name) title, success/error state)
- Register SkillToolCall in ToolCallBlock for tool names 'skill' and 'load_skill'
- In ChatPaneInterface.handleSend: extract custom command chip names from content,
strip leading / from message text, pass names as metadata.skills to sendMessage
- Add skills?: string[] to sendMessageInput metadata schema (zod.ts)
- Pass preloadSkills to harness.sendMessage in service.ts
- Add ChatSendMessageInput.metadata.skills type field
- Add docs/skill-preload-feature.md with implementation state and setup instructions
Requires superset-sh/mastra#9 for harness.sendMessage preloadSkills support
and .claude/commands/ being included in skillPaths.
* feat(ui): update shared AI element components for chat UX
- braille-spinner: improve animation timing
- code-block: add copy button and syntax highlight tweaks
- message: simplify prose class handling
- prompt-input: add focusShortcutText prop support
- tool-call-row: tighten collapsible layout and spacing
- input-group: support rounded-full variant
- globals.css: add chat-specific scrollbar and prose overrides
* fix(chat): improve tool call display components
- AskUserQuestionToolCall: redesign option layout with better button styling
- SupersetToolCall: render markdown content in tool output
- SubagentToolCall/SubagentInnerToolCall: tighten display, fix edge cases
- TaskWriteToolCall: simplify status rendering
- ReadOnlyToolCall: add workspace prop forwarding and memoize file query
- QuestionInputOverlay: improve option button layout
- MessageList: remove unused prop
* fix(chat): update message list and subagent execution display
- ChatMessageList: update subagent message grouping and rendering
- SubagentExecutionMessage: improve tool call display during subagent runs
- messageListHelpers: refine pending/streaming message detection
- use-chat-display: minor hook cleanup
- screens/main ChatPaneInterface: propagate workspace props
* fix(chat): suppress empty assistant message wrappers
When an assistant message has no renderable content (e.g. redacted_thinking
or unrecognized AI SDK step markers), return null instead of rendering
empty Message/MessageContent divs that cause blank gaps between messages.
* feat(chat): show styled "Not Configured" state for LSP inspect when LSP is absent
Detect the "LSP is not configured for this workspace" error and surface it
as a red "Not Configured" badge in the tool call row rather than triggering
the generic error styling.
* feat(chat): universal "not configured" warning on tool call rows
Detect "not configured" errors in getGenericToolCallState and surface
them as a filled amber warning triangle with a "Not configured" tooltip
in the ToolCallRow status area. File name description is preserved.
GenericToolCall passes isNotConfigured through so the treatment applies
to all tool calls, not just LSP Inspect.
* fix(chat): move not-configured warning icon inline after description
Show the outlined amber TriangleAlertIcon next to the file name in the
description area instead of in the right-side status slot.
* feat(chat): clickable file names on file-related tool call rows
Replace the standalone open-in-pane icon button with a hover-underline
treatment directly on the filename. Applies to Read, Check file, Write,
Edit, Delete, and Smart Edit tool call rows.
Extracts a shared ClickableFilePath component (span[role=button]) that
nests safely inside CollapsibleTrigger without invalid nested-button HTML.
* feat(ui): add ShowCode component with expand/collapse, copy, and startLine support
Adds a new ShowCode component that unifies all code display surfaces — tool
call file views and markdown code fences — into a single block with:
- Filename/language header with optional clickable file path and line range
- Expand/collapse toggle (appears when content exceeds ~15 lines)
- Copy and open-in-pane action buttons in the header
- startLine offset for partial-file display (line numbers count from the
correct offset rather than always starting at 1)
- Language fallback in highlightCode: unknown Shiki languages silently
retry as "text" instead of throwing
* refactor: replace legacy syntax highlighters with ShowCode
- ReadFileTool: swap inline CodeBlock + duplicated header/button JSX for
a single <ShowCode> — removes the fragile [&>div>div]:max-h-[300px]
deep selector and the duplicated open-in-pane button pattern flagged in
code review
- MarkdownRenderer/CodeBlock (desktop): replace react-syntax-highlighter
(Prism) with ShowCode, aligning markdown code fences with the shared
Shiki-based highlighter used throughout the chat UI
* fix(chat): address PR review feedback
- Move useMemo hooks before early returns in AskUserQuestionToolCall and
SubagentInnerToolCall to fix React Rules of Hooks violations
- Use hasFileContent (content !== undefined) guard in ReadOnlyToolCall
so empty files render in the code viewer instead of falling back
- Tighten @mention regex to require word-boundary before @ so email
addresses and decorators are not rewritten as file mentions on round-trip
- Add trigger to ScrollAnchor useEffect deps so footerScrollTrigger bumps
actually retrigger the scroll effect
- Add pendingQuestion to bumpFooterScroll useEffect deps in ChatPaneInterface
so the chat scrolls when the question overlay appears or disappears
- Roll back optimistic answeredQuestionId and pane status if
respondToQuestion RPC fails in legacy ChatPaneInterface
- Guard Shiki highlightCode fallback so it does not recurse infinitely
when language === "text" already
- Add aria-label to icon-only buttons (expand/collapse, open, copy) in ShowCode
- Remove dead _interruptedByAbortedQuestion useMemo from legacy ChatMessageList
* fix(chat): address second round of PR review feedback
- Add hasPendingQuestionToolCall to workspace path messageListHelpers so
assistant messages with active ask_user calls stay visible during a run
- Reset QuestionInputOverlay state (customText, submittedLabel) when the
question prop changes identity, not just on mount
- Fix Enter/Tab in file-mention mode only consuming the event when a file
is actually selected; falls through to normal submit when results are empty
- Keep isError indicator visible in ToolCallRow status slot when the row is
expanded (previously the error icon disappeared on open)
* fix(chat): address third round of PR review feedback
- Fix colorize=false fading first code token when line numbers are
disabled: add a shiki-line-number class to gutter spans and target
that class instead of span:first-child in the CSS selector
- Add e.preventDefault() to Space key handler in ClickableFilePath
so activating via keyboard does not also scroll the container
- Fix file mention round-trip for paths containing spaces: serializer
now emits @"path with spaces" and parser handles both quoted and
unquoted forms
- Add null guard in FileMentionNode so malformed/pasted content with
a missing path attr does not crash rendering
* fix(chat): address fourth round of PR review feedback
- Fix ReadOnlyToolCall lineRange: disk read always returns the whole
file so always display 1–N (trimming trailing newline before counting)
- Fix Shiki fallback to render escaped plain text instead of empty
strings when codeToHtml fails for the "text" language itself
- Trim trailing newline before computing lineCount in ShowCode so files
ending with \n do not trigger isOverflowing one line early
* chore: formatting and cleanup
* fix(chat): close mention popup before falling through Enter when results empty
* fix(chat): address fifth round of PR review feedback
- Re-add inputValue to SlashCommandPreviewPopover anchor effect deps so
the virtual anchor re-measures when typing shifts the chip's position
- Clamp slash menu selectedIndex in onUpdate when filtered results shrink,
matching the existing mention-menu clamping behavior
- Return true (consume event) when Enter/Tab closes empty mention popup so
the event does not propagate to insert a paragraph break
- Mirror focus-on-dismiss effect in v2 workspace ChatInputFooter so the
editor regains focus after the question overlay unmounts
- Fix trailing-slash paths rendering empty label in ClickableFilePath
by using || instead of ?? for the basename fallback
* chore: rebuild bun.lock after rebase
* Fix typecheck
* refactor(chat): align skill handling with upstream mastra
Removes the fork-dependent preload wiring (metadata.skills →
preloadSkills pass-through) that was a silent no-op on upstream
mastracode. Keeps the SkillToolCall renderer so load_skill tool
calls emitted by upstream's native skills system render with their
own UI.
Rewrites docs/skill-preload-feature.md to describe the upstream
agent-autonomous model (SKILL.md discovery in .claude/skills,
.agents/skills, .mastracode/skills).
* chore(deps): bump mastra to 0.15.0-alpha.3 / 1.26.0-alpha.3
Brings in upstream mastra's native skills system (search_skills +
load_skill tools, SKILL.md discovery via skillPaths) which the
SkillToolCall renderer in this PR now consumes for free.
- mastracode: 0.14.0 → 0.15.0-alpha.3
- @mastra/core: 1.25.0 → 1.26.0-alpha.3
- @mastra/mcp: 1.3.1 → 1.5.1-alpha.1
Applied in apps/desktop, packages/chat, packages/host-service.
* chore: alphabetize @tiptap/pm in desktop package.json
Auto-applied by biome/sherif.
* fix(chat): cut display polling to 4fps and restore query cache defaults (superset-sh#3562)
Memory leak and CPU spiral root-caused to `staleTime: 0, gcTime: 0` +
60fps polling: React Query can't dedupe or GC anything, and the render
path churns allocations every 16ms.
Restoring the React Query defaults (5min gcTime) fixes the leak. Server
poll rate is independent of perceived stream smoothness — StreamingMessageText
already reveals text client-side at 60fps from whatever buffer the server
delivers. 4fps polling keeps that buffer fed with plenty of headroom.
Also removes the `isRunning` invalidation effect — redundant when the
query is polling.
Builds on and supersedes superset-sh#3170 by @thepathmakerz, which diagnosed the
same root cause. This version takes the subtractive path (-21 lines)
instead of adaptive polling (+36).
Closes superset-sh#3049
* feat(chat): slash command chip UX enhancements
- Argument editing inline in chip: auto-focus on insert, right-arrow to exit, double-click to re-enter
- Commands without argumentHint hide the colon/input entirely
- Model command shows a dropdown of available models (no free-form text)
- Chip input auto-sizes as user types (shrinks to content width)
- Dropdown positioned above chip (side="top"), ArrowUp/Down navigate options, Tab/Enter commit selection
- Menu reopens automatically when deleting value back to empty
- Preview popover and select dropdown are mutually exclusive (preview only on hover/node-select, never while arg input is focused)
- Focus shortcut hint moved inside TiptapPromptEditor (accepts focusShortcutText prop)
* chore: refresh bun.lock after pull
* test(chat): drop shallow ChatMessageList snapshot tests
These tests replace every child component with a mock placeholder
and assert on literal strings appearing in the rendered HTML. That
tested mock plumbing, not behavior — every SUT import change broke
them regardless of whether the actual render output changed, and
the 'SUBAGENT_EXECUTION_MESSAGE' assertion was checking for a
component this PR intentionally inlined.
The useful bit (filter/ordering logic in messageListHelpers) is
better covered by a direct unit test — leaving as a follow-up.
…uperset-sh#3565) Observability was enabled in superset-sh#1464 but dropped when the proxy was re-created from scratch in superset-sh#1867. Without it, every wrangler deploy reconciles Cloudflare back to logs/traces off, which is why the dashboard toggles kept reverting after each production deploy. Pin invocation_logs explicitly so future config drift can't silently disable it again. Audit logs are an account-level setting and still need to be re-enabled in the Cloudflare dashboard separately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Latest Upstream Release: Superset Desktop desktop-v1.5.6
Tag:
desktop-v1.5.6Published: 2026-04-18T12:53:39Z
Release Notes
What's Changed
Full Changelog: superset-sh/superset@desktop-v1.5.5...desktop-v1.5.6
209 new commits from upstream.
This PR is automatically created and updated daily to keep the fork in sync with superset-sh/superset.