Move update downloads into the background and prompt only after the update is ready, so the global update UI no longer pins a progress panel over the app. Keep the About page as the detailed status surface and make failed downloads or installs retryable without losing the prepared update.
Constraint: Tauri updater install still needs an explicit user restart action after download
Rejected: Keep showing global download progress | it preserves the blocking top-right experience that triggered the complaint
Confidence: high
Scope-risk: moderate
Directive: Do not show the global update prompt before status is downloaded without rechecking the desktop UX complaint
Tested: bun run test -- src/stores/updateStore.test.ts src/components/shared/UpdateChecker.test.tsx --run
Tested: bun run check:desktop
Tested: bun run check:native
Tested: bun run check:coverage
Tested: bun run verify
Not-tested: Real signed release artifact update from an installed older desktop build
Desktop destructive confirmations had split implementations: shared ConfirmDialog, hand-rolled modals, inline task popovers, and a diagnostics window.confirm path. This adds shared ActionDialog and ConfirmPopover primitives, keeps ConfirmDialog as the simple two-action wrapper, and migrates the known desktop confirmation flows onto those shared surfaces.
Constraint: Running-tab close needs three actions, so the two-button ConfirmDialog API was not enough.
Rejected: Force every confirmation through ConfirmDialog | would either lose the keep-running action or make ConfirmDialog too broad for inline popovers.
Confidence: high
Scope-risk: moderate
Directive: Add new desktop confirmation flows through ActionDialog, ConfirmDialog, or ConfirmPopover instead of hand-rolled overlays.
Tested: cd desktop && bun run test -- AdapterSettings.test.tsx mcpSettings.test.tsx TabBar.test.tsx PermissionModeSelector.test.tsx diagnosticsSettings.test.tsx
Tested: bun run check:desktop
Tested: git diff --check
Tested: Chrome DevTools smoke for provider delete and diagnostics clear-log confirmation dialogs
Not-tested: Agent Browser smoke because the local agent-browser CLI hung on open/snapshot/doctor/close commands.
Message action controls stay visible through focus-within for keyboard navigation, but pointer clicks left the copy/fork buttons focused after the mouse moved away. Release pointer focus on message actions so hover controls return to their quiet state while keeping Tab focus behavior intact.
Constraint: Message actions use focus-within for keyboard accessibility and should not remove that path.
Rejected: Remove focus-within reveal | would regress keyboard users who tab to copy or fork.
Confidence: high
Scope-risk: narrow
Directive: Keep pointer-only blur scoped to message action controls; do not remove focus-within without a replacement keyboard affordance.
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --run
Tested: bun run check:desktop
Not-tested: Live Tauri manual hover smoke
Related: #642
Message rows should stay visually quiet until the user needs copy or fork controls. The action row now appears on message hover/focus, uses icon-sized controls, and shows the message timestamp beside the actions with locale-aware recent and historical formats.
Constraint: Desktop chat actions already own copy and fork affordances, so the change keeps that surface instead of adding a separate always-visible timestamp row.
Rejected: Keep text-labeled Copy buttons | the row remained too visually heavy for every message.
Confidence: high
Scope-risk: narrow
Directive: Keep copy, fork, and timestamp as one hover/focus metadata row unless the message layout is redesigned as a whole.
Tested: bun run check:desktop
Not-tested: Live Tauri native packaging
The shared directory picker is used from right-aligned controls such as the MCP target project selector. Its portal menu previously used the trigger's raw left coordinate, so a trigger near the window edge could render the 400px menu partly off-screen.
Clamp the fixed-position menu against the viewport while preserving the existing width where space allows, and cover the right-edge case with a focused component test.
Constraint: DirectoryPicker is shared by MCP settings, task prompts, adapter settings, and launch controls.
Rejected: Special-case the MCP form layout | the overflow comes from the shared portal positioning and would remain in other right-aligned uses.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- src/components/shared/DirectoryPicker.test.tsx
Tested: bun run check:desktop
Not-tested: Manual browser smoke; local agent-browser automation hung during startup/command evaluation.
Desktop worktree sessions should show the source project and worktree marker without surfacing implementation refs like worktree-desktop-* as user-facing branch state. The git-info response now keeps launch branch metadata separate from worktree identity, and the desktop chip hides branch and slug labels in isolated worktree mode.
Constraint: Git worktrees need an internal branch for isolated execution, but that ref is product plumbing rather than useful UI context
Rejected: Show both launch branch and worktree slug | duplicated noisy identifiers and confused the session location
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/sessions.test.ts -t "git-info"; cd desktop && bun run test src/components/shared/ProjectContextChip.test.tsx; bun run check:server; bun run check:desktop; git diff --check
Not-tested: Live desktop screenshot smoke
The empty-session desktop composer rendered repository launch context
as a separate footer, which created a visual gap between the prompt
surface and the project/branch/worktree controls. This keeps the PC
layout in one panel while preserving the existing mobile bottom layout.
Constraint: Mobile H5 composer layout must remain unchanged.
Rejected: Restyle the shared launch bar globally | existing standalone and mobile placements rely on the old chrome.
Confidence: high
Scope-risk: narrow
Directive: Keep the composer placement desktop-only unless mobile layout is intentionally redesigned.
Tested: cd desktop && bun run test src/pages/EmptySession.test.tsx src/components/shared/RepositoryLaunchControls.test.tsx src/components/shared/DirectoryPicker.test.tsx
Tested: bun run check:desktop
Tested: agent-browser screenshots for PC and mobile empty-session composer.
Not-tested: Packaged Tauri runtime smoke.
The sidebar had only single-session deletion, which made stale or noisy
session lists expensive to maintain. This adds a batch-management lane in
the desktop UI and a server endpoint that deletes multiple sessions while
preserving per-session failure reporting and cleanup behavior.
Constraint: Session deletion must preserve existing transcript and adapter cleanup semantics
Constraint: Desktop UI should reuse the shared confirmation dialog instead of introducing a second modal surface
Rejected: Delete all selected sessions through repeated client DELETE calls | weaker partial-failure handling and duplicated cleanup orchestration
Rejected: Use a generic checkmark entry icon | it did not communicate batch deletion clearly enough
Confidence: high
Scope-risk: moderate
Directive: Keep batch deletion routed through the server batch endpoint so rollback and adapter cleanup stay centralized
Tested: bun test src/server/__tests__/sessions.test.ts -t batch-delete
Tested: bun run check:server
Tested: cd desktop && bun run check:desktop
Tested: browser UI smoke for group select, shift range select, delete confirmation, 7/30 day cleanup, Cmd+A filtered selection, and Escape exit
Not-tested: Live provider-backed session generation; this change covers session list management after sessions already exist
Packaged desktop builds started the sidecar on loopback only, so phones on the same WiFi could not connect to the H5 URL. The server now binds to LAN interfaces while keeping the desktop control URL on loopback, serves the bundled H5 frontend, and reports a LAN public URL when H5 access is enabled.
Constraint: Packaged Tauri resources place ../dist under Contents/Resources/_up_/dist.
Rejected: Bind the desktop control URL to 0.0.0.0 | desktop clients should keep using loopback for local control.
Confidence: high
Scope-risk: moderate
Directive: Keep packaged H5 resource lookup aligned with Tauri resource mapping when changing bundle resources.
Tested: bun run check:server
Tested: cd desktop/src-tauri && cargo test --lib
Tested: cd desktop && bun run test -- desktopRuntime.test.ts generalSettings.test.tsx
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Packaged claude-sidecar smoke served H5 HTML while listening on *:39876
Not-tested: Physical phone browser test on the local WiFi network
H5 browser use needs to keep local desktop WebUI testing open while giving remote phones a usable chat-first surface. This change scopes browser auth by origin and target host, hides non-chat desktop chrome on H5 mobile, compacts the empty-session composer, and unifies mobile selectors behind a full-width bottom sheet with an explicit close action.
Constraint: Local browser development may bypass H5 auth only for loopback or private LAN server hosts; public or remote hosts still require an H5 token.
Constraint: Mobile H5 scope is chat-first; settings and scheduled-task surfaces stay hidden instead of fully redesigned.
Rejected: Reuse desktop popovers on mobile | hover/anchored dropdown behavior leaves key actions hard to dismiss and cramped on narrow screens.
Rejected: Let any localhost origin bypass remote auth | it would let arbitrary local pages access public H5 servers without a token.
Confidence: medium
Scope-risk: moderate
Directive: Keep future H5 selector popups on MobileBottomSheet unless a selector needs a reviewed custom mobile interaction.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx src/lib/desktopRuntime.test.ts src/components/shared/RepositoryLaunchControls.test.tsx src/components/controls/PermissionModeSelector.test.tsx src/components/controls/ModelSelector.test.tsx src/components/shared/DirectoryPicker.test.tsx src/__tests__/pages.test.tsx
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: Chrome mobile viewport smoke for context, model, permission, project, branch, and worktree bottom sheets with no horizontal overflow.
Not-tested: Full bun run verify quality gate.
The footer project chip previously kept showing only the source project and selected branch even after a session launched inside an isolated CLI worktree. That made an active worktree session look identical to a normal checkout, especially while agent tools were already operating under .claude/worktrees. The server now returns explicit worktree metadata from the session launch intent, and the desktop chip preserves the user-facing project and branch while appending the isolated worktree slug.
Constraint: CLI worktree branches are internal implementation details and should not replace the user-selected branch label.
Rejected: Show only the actual worktree git branch | it would expose worktree-desktop-* internals and make the business branch harder to read.
Confidence: high
Scope-risk: narrow
Directive: Keep normal checkout footer rendering unchanged; only add worktree identity when session metadata says isolation is enabled.
Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern git-info
Tested: cd desktop && bun run test -- src/components/shared/ProjectContextChip.test.tsx
Tested: cd desktop && bun run lint
Tested: git diff --check
Desktop repository launches now defer isolated worktree creation until the
first user turn so the CLI owns worktree setup, cwd initialization, and
session metadata. The chat UI surfaces the pre-startup Git phase so users see
when a session is creating a worktree or switching a branch before model
output begins.
Constraint: Desktop must preserve the selected source checkout until a user actually sends a message
Constraint: CLI setup is the canonical owner for worktree creation and cwd initialization
Rejected: Create the worktree eagerly in the desktop session picker | it diverges from CLI session startup and creates worktrees before a conversation exists
Confidence: high
Scope-risk: moderate
Directive: Keep repository session startup routed through CLI worktree flags; do not reintroduce eager desktop worktree creation without testing transcript cwd and cleanup behavior
Tested: bun test src/server/__tests__/sessions.test.ts src/server/__tests__/conversation-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "worktree startup status"
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Tested: CLI init-only native worktree smoke from feature/rail
Tested: agent-browser UI flow with MiniMax-M2.7-highspeed in a /tmp repository
Non-Git folders are valid launch targets for desktop sessions, so the composer should not render them as an inline repository warning. The branch and worktree controls remain hidden outside Git repositories while server-side repository validation still protects actual branch-launch requests.
Constraint: Non-Git directories must remain usable as ordinary session workdirs.
Rejected: Keep the inline not-Git warning | it reads like an error even though the user can continue normally.
Confidence: high
Scope-risk: narrow
Directive: Keep dirty-worktree and checked-out-branch warnings visible because those still require a launch-mode decision.
Tested: cd desktop && bun run test -- --run src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx
Tested: bun run check:desktop
Tested: git diff --check -- desktop/src/components/shared/RepositoryLaunchControls.tsx desktop/src/pages/EmptySession.test.tsx desktop/src/i18n/locales/en.ts desktop/src/i18n/locales/zh.ts
Not-tested: Native Tauri packaged window visual check
Long branch names should be clipped inside the branch selector instead of forcing the repository context rail onto a second row. The rail now stays nowrap, the branch selector owns truncation, and the worktree mode selector keeps its readable fixed footprint.
Constraint: Preserve the existing composer and repository context layout shape.
Rejected: Let the rail wrap on long branch names | it creates the broken two-line state shown in the desktop composer.
Confidence: high
Scope-risk: narrow
Directive: Long repository or branch labels must truncate inside their own controls rather than changing the rail height.
Tested: cd desktop && bun run test -- --run src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx src/components/shared/DirectoryPicker.test.tsx
Tested: bun run check:desktop
Tested: agent-browser selected a long branch on http://localhost:4790 and verified rail height 53px, branch width 260px, no horizontal overflow; screenshot /tmp/cc-haha-launch-rail-long-branch-nowrap.png
Not-tested: Native Tauri packaged window visual check
The worktree control is a mode choice, so it should behave like the branch selector instead of a hidden toggle. This adds a compact dropdown with current and isolated worktree options while preserving the existing launch layout and isolation guard.
Constraint: Keep the composer context rail layout unchanged.
Rejected: Keep the single-click toggle | the interaction hid the available modes and made the state harder to reason about.
Confidence: high
Scope-risk: narrow
Directive: Worktree mode must stay an explicit selection surface, not a hidden toggle.
Tested: cd desktop && bun run test -- --run src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx src/components/shared/DirectoryPicker.test.tsx
Tested: bun run check:desktop
Tested: agent-browser worktree dropdown desktop and mobile screenshots at /tmp/cc-haha-worktree-dropdown-menu.png, /tmp/cc-haha-worktree-dropdown-selected.png, /tmp/cc-haha-worktree-dropdown-mobile-menu.png, /tmp/cc-haha-worktree-dropdown-mobile-selected.png
Not-tested: Native Tauri packaged window visual check
The composer context rail should read as one compact group, not three stretched controls. This change removes flex growth from the repository picker, caps branch width by content, softens the active worktree state, and removes hover borders that made secondary state controls look primary.
Constraint: Keep the existing composer and session layout unchanged.
Rejected: Use a prominent active outline for worktree | it made the state look like the primary action.
Confidence: high
Scope-risk: narrow
Directive: Keep this rail content-sized; long names should truncate inside chips instead of spreading across the bar.
Tested: cd desktop && bun run test -- --run src/components/shared/DirectoryPicker.test.tsx src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: bun run check:desktop
Tested: agent-browser desktop screenshot /tmp/cc-haha-repo-controls-compact-worktree-active-final.png and mobile overflow check /tmp/cc-haha-repo-controls-compact-mobile-final.png
Not-tested: Native Tauri packaged window visual check
The launch controls live directly under the composer, so they need to read as a compact context rail rather than a second card. The styling now uses a lighter surface, shorter controls, bounded flex sizing, and explicit hover titles for long repository and branch names. Git repo status is passed into the picker so a freshly opened Git project does not briefly look like a plain folder.
Constraint: Keep the existing session/composer layout untouched; only refine the context bar under the input.
Rejected: Redesign the empty session layout | user explicitly scoped this to the lower context bar.
Confidence: high
Scope-risk: narrow
Directive: Do not grow this rail vertically without browser-checking long repo and branch names.
Tested: cd desktop && bun run test -- --run src/components/shared/DirectoryPicker.test.tsx src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: bun run check:desktop
Tested: agent-browser long repo and long branch screenshots at /tmp/cc-haha-repo-controls-polished-git-long-branch-selected.png and /tmp/cc-haha-repo-controls-polished-mobile-long.png
Not-tested: Native Tauri packaged window visual check
The repository launch controls should extend the existing empty-session composer, not replace the session layout. This restores the previous mascot, title, composer sizing, textarea, and run button while keeping the project, branch, and worktree controls attached directly beneath the input.
Constraint: User requested the original session layout stay intact except for adding repository controls under the chat input
Rejected: Keep the Codex-style centered launcher rewrite | it changed too much of the existing session surface
Confidence: high
Scope-risk: narrow
Directive: Future changes to repository launch controls should treat EmptySession layout as fixed unless explicitly requested otherwise
Tested: cd desktop && bun run test -- --run src/components/shared/DirectoryPicker.test.tsx src/pages/EmptySession.test.tsx src/__tests__/pages.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: agent-browser screenshot /tmp/cc-haha-layout-restored-selected.png on localhost:4790
Not-tested: root bun run verify not rerun for this narrow layout correction
The empty-session launch surface needed to behave like a real coding-agent entry point: project, branch, and worktree choices are now visible together, and branch choices that cannot safely run in-place force an isolated worktree before session creation.
Constraint: Desktop launch flows must preserve repository safety when a target branch is dirty or checked out elsewhere
Rejected: Keep branch and worktree controls as detached chips | the selection state was easy to miss and visually inconsistent with the launcher
Confidence: high
Scope-risk: moderate
Directive: Do not remove the automatic worktree requirement without rechecking dirty and checked-out branch launches through the desktop UI
Tested: cd desktop && bun run test -- --run src/components/shared/DirectoryPicker.test.tsx src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: browser-driven desktop smoke on ports 4789/4790
Not-tested: bun run verify still has the pre-existing agent-utils coverage ratchet failure outside desktop
The desktop test package was able to reuse stale Tauri target output even after the frontend and sidecar were rebuilt, which made UI changes look missing after a build. The local macOS build script now clears that cache by default while retaining an opt-in incremental path.
The project selector also no longer repeats the active branch because branch and worktree are now first-class launch controls beside it.
Constraint: Local macOS test packages must prioritize freshness over incremental build speed
Rejected: Keep branch text in the project chip | duplicated the dedicated branch selector and made the launch row harder to scan
Confidence: high
Scope-risk: narrow
Directive: Keep branch selection behavior in RepositoryLaunchControls; DirectoryPicker should only identify the project
Tested: cd desktop && bun run test -- DirectoryPicker.test.tsx
Tested: cd desktop && bun run lint
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Opened built app and confirmed sidecar /health returned ok
Desktop sessions now resolve repository launch intent before creation, so users can pick a branch, choose whether to isolate it in a worktree, and get stable error messages instead of silent branch-switch failures. The server owns the Git safety checks and session metadata so the UI and real agent runtime agree on the actual working directory.
Constraint: Direct branch switching must not overwrite or hide uncommitted user changes
Constraint: Desktop worktree branches must stay out of normal branch selection and recent-project labels
Rejected: Let the UI call git directly | server-side checks keep session metadata and runtime launch paths consistent
Rejected: Auto-switch dirty checkouts | users need an explicit isolated-worktree choice to protect local edits
Confidence: high
Scope-risk: moderate
Directive: Do not relax dirty-worktree or checked-out-branch guards without adding equivalent business-flow tests
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: /tmp business-flow script with dirty checkout, checked-out branch, isolated worktree, missing branch, non-git, missing directory, and real LLM session
Tested: agent-browser desktop UI flow across dirty, checked-out, non-git, branch search, recent-project, and Run scenarios
The chat surface now relies on the current-turn changes card for file rollback, so the older per-message hover rewind affordance was removed to avoid two competing rollback models.
Constraint: Current-turn undo still depends on the existing checkpoint rewind API.
Rejected: Keep both rewind entry points | duplicate rollback affordances make the product harder to explain and test.
Confidence: high
Scope-risk: moderate
Directive: Prefer adding rollback behavior through the current-turn change card instead of restoring per-message hover rewind.
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: cd desktop && bun run test
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Browser E2E scripts were updated but not executed in this commit.
Expose a workspace inspector for desktop sessions so users can browse the active project, preview files, inspect session-derived changes, and review diffs even when the work directory is not a git repository.
Constraint: The workspace view must work for temporary and non-git project directories.
Rejected: Rely only on git status | non-git sessions would lose the changed-files surface.
Confidence: medium
Scope-risk: moderate
Directive: Keep undo semantics separate from workspace browsing; checkpoint-backed rewind should remain the source of truth for rollback.
Tested: bun test src/server/__tests__/sessions.test.ts src/server/__tests__/workspace-service.test.ts
Tested: cd desktop && bun run test -- src/components/workspace/WorkspacePanel.test.tsx src/stores/workspacePanelStore.test.ts
Tested: cd desktop && bun run lint
Tested: git diff --check
Not-tested: Full desktop packaged app build after this commit
Third-party Anthropic-compatible providers can expose Claude model names without supporting every first-party runtime capability. Desktop sessions now preserve provider-specific capability overrides, keep selected provider runtime state across reconnects and restarts, and keep the provider dialog behavior aligned with the updated presets.
Constraint: Custom ANTHROPIC_BASE_URL endpoints must not be treated as first-party Anthropic for adaptive thinking support.
Rejected: Rename provider default Sonnet models | that would hide the compatibility issue instead of fixing runtime capability detection.
Confidence: high
Scope-risk: moderate
Directive: Do not enable Claude first-party capability defaults for custom Anthropic-compatible base URLs without provider verification.
Tested: bun test src/utils/__tests__/thinking.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test src/components/shared/Modal.test.tsx --run
Tested: git diff --check
Provider setup now carries the metadata needed for sponsored and local Anthropic-compatible providers, while keeping provider URLs and API keys editable in the desktop form. The desktop UI also exposes API-key links, optional sponsor copy, full key visibility, and local no-key presets that can be activated into runtime settings.
Constraint: Local LM Studio and Ollama integrations require Anthropic-compatible root URLs rather than OpenAI /v1 URLs
Rejected: Keep local providers at the top of the preset list | user requested them immediately before Custom
Confidence: high
Scope-risk: moderate
Directive: Do not change local provider base URLs to /v1 without rechecking Anthropic compatibility docs
Tested: bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test src/__tests__/generalSettings.test.tsx --run
Tested: git diff --cached --check
Tested: Web UI provider creation plus real sessions for LM Studio, Ollama, JiekouAI, and Shengsuanyun
Not-tested: Production packaged Tauri build
The desktop settings and sidebar still had deletion flows that either used browser-native confirms or deleted immediately. This change consolidates destructive confirmations behind a shared dialog component, applies it to provider deletion, plugin uninstall, adapter unbind, and sidebar session deletion, and adds regression coverage so delete actions require an explicit second confirmation before mutating state.
Constraint: Other in-progress desktop work in the tree had to stay out of this commit
Constraint: Existing MCP and task confirmations needed to keep their current behavior
Rejected: Leave confirmations embedded per-page with browser dialogs | inconsistent UX and easy to regress
Rejected: Add confirmations only to provider deletion | leaves other destructive desktop flows unsafe
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any new desktop delete, uninstall, or unbind action should use the shared ConfirmDialog instead of browser-native dialogs or one-click deletion
Tested: bun run test src/components/layout/Sidebar.test.tsx src/__tests__/generalSettings.test.tsx; bun run lint
Not-tested: Manual desktop click-through of every destructive action after this refactor
The desktop updater now renders release notes as markdown, avoids fake 0%
progress when the server omits Content-Length, and remembers when the user
has dismissed a specific release prompt so reopening the app does not nag
again for the same version.
Constraint: Existing 0.1.4 clients can receive updater events without total size metadata and users still need a manual update path in About
Rejected: Keep repeating the prompt on every launch | creates avoidable noise after an explicit later decision
Rejected: Global dismiss flag for all future releases | would hide newer versions that should prompt again
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep prompt suppression keyed to availableVersion only; About page visibility and manual update actions must remain available
Tested: bun run test src/stores/updateStore.test.ts src/components/shared/UpdateChecker.test.tsx src/__tests__/generalSettings.test.tsx; bun run lint; manual local updater validation from 0.1.4 to 0.1.5 on /Applications and an extracted v0.1.4 release bundle
Not-tested: Signed and notarized macOS distribution behavior outside this local machine
This introduces a persisted light/dark appearance setting, maps the desktop shell onto semantic theme tokens, and reworks the highest-traffic chat/settings surfaces so the new dark mode is usable without regressing the original light theme.
The same pass tightens markdown rendering for chat replies by improving inline code, table overflow handling, and safe external-link behavior so dark-mode content stays legible in real conversations.
Constraint: Preserve the existing light theme while adding a user-selectable dark theme in Settings > General
Constraint: Avoid introducing new dependencies for styling or markdown handling
Rejected: Replacing the light palette with a single dual-purpose palette | would risk broad visual regressions across the existing desktop UI
Rejected: Implementing dark mode only for shell chrome | leaves chat markdown, diffs, and permission flows visually broken
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: New desktop UI should use semantic theme variables instead of hard-coded color literals so both themes stay aligned
Tested: bun run lint; bun run test; bun run build; browser review of theme switching, provider/permission states, and chat surfaces
Not-tested: Prototype-style pages with remaining hard-coded colors (scheduled/session control mock surfaces) were not fully normalized in this change
The desktop app already had Tauri updater plumbing, but the
release pipeline was not emitting signed updater artifacts and
the UI exposed only a thin auto-check path. This change restores
a working updater release path, rotates to a new updater public
key, and adds a shared update flow with manual check/install
controls for testing.
Constraint: Original updater private key is unavailable, so a new public key had to be embedded and old installs cannot trust new signatures
Constraint: Must not add new dependencies or require main-branch rollout before validation
Rejected: Keep the old pubkey and skip signing | would leave release builds unable to publish valid updater artifacts
Rejected: Add a manual download fallback flow | user explicitly deferred that work
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Preserve the new updater private key and password outside the repo; losing them again will break future in-app updates for installed builds
Tested: desktop unit test for updater store; desktop TypeScript no-emit; desktop production build
Not-tested: end-to-end updater install against a real GitHub Release on this branch
- security: XSS sanitization with DOMPurify in Markdown/Mermaid/PermissionDialog;
path whitelist in filesystem API; fake keys in test/config files
- perf: fine-grained Zustand selectors in Sidebar/StatusBar/ContentRouter;
50ms throttle on streaming deltas; React.memo + useMemo in MessageList;
useRef for frequent keyboard shortcut state; AbortController 30s timeout
- leaks: WS session TTL timers (5-min cleanup on close); batch splice for
sdkMessages/stderrLines; folderPath validation in cronScheduler
- quality: optimistic update rollback in settingsStore; error state in
providerStore/teamStore; i18n for all hardcoded English strings
- docs: desktop architecture and features docs updated; VitePress nav fixed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The desktop chat view flattened Agent tool activity, which made sub-agent work
hard to follow and separated key evidence from the main conversation. This
change threads parent tool linkage through the server bridge and desktop store,
renders dispatched sub-agents as grouped cards with nested tool activity, and
moves long final outputs into a markdown preview dialog so the main transcript
stays readable on narrow layouts.
Constraint: Existing sessions and live websocket events both needed to preserve parent-child relationships
Rejected: Add brand-new subagent websocket event types | unnecessary protocol expansion when parent linkage already existed upstream
Rejected: Inline full sub-agent markdown in the card body | too cramped for narrow desktop chat layouts
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep Agent card summaries compact; route long-form sub-agent output through the preview dialog unless the main chat layout is widened substantially
Tested: cd desktop && bun run test -- MessageList.test.tsx chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/sessions.test.ts -t should\ reconstruct\ parent\ agent\ tool\ linkage\ from\ parentUuid\ chains
Not-tested: Full end-to-end visual verification against live CLI sessions with sub-agent text/thinking nested inline
The session view exposed copy affordances in a way that implied the
whole conversation would be copied, while user prompts had no copy
path at all. This changes copy to a per-message footer action so the
scope is explicit for both prompts and assistant replies, and reuses
the shared clipboard helper instead of maintaining a second copy path.
Constraint: The worktree contains unrelated in-progress changes that must stay uncommitted
Rejected: Keep a floating action above each message | it interrupted reading order and still looked session-scoped
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep copy affordances attached to individual message blocks so the copied scope stays obvious
Tested: bun run test src/components/chat/MessageList.test.tsx; bun run lint
Not-tested: Manual desktop visual QA across dense multi-message sessions
Implement IM adapters allowing users to chat with Claude Code from Telegram
and Feishu/Lark. Includes persistent session management (chatId→sessionId
mapping), project selection via /projects command, and a web UI settings page
for configuring bot tokens, allowed users, and default project directory.
Key changes:
- adapters/: Telegram and Feishu adapter scripts with shared common modules
(WsBridge, MessageBuffer, SessionStore, HttpClient, config, formatting)
- Backend: adapterService + REST API (GET/PUT /api/adapters) with secret masking
- Frontend: AdapterSettings page in Settings tab with i18n support
- DirectoryPicker: use React Portal for dropdown to fix overflow clipping
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a lightweight custom i18n system supporting English (default)
and Chinese, with a language switcher in Settings > General.
All 35+ UI components internationalized with ~270 translation keys,
including 189 Chinese spinner verbs and server error code mapping.
DirectoryPicker now detects runtime environment:
- Tauri desktop: opens native OS folder picker via @tauri-apps/plugin-dialog
- Web browser: falls back to backend API directory tree browser
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The desktop app now keeps the composer stable while turns are active,
reduces low-signal tool noise in the transcript, restores project context
under the composer after session creation, and relies on the CLI's own
permission requests instead of injecting broader desktop-side Bash asks.
This also brings in the supporting desktop app source tree and the server
routes/session metadata needed for git info, filesystem browsing, session
resume, slash commands, and SDK-backed permission bridging so the UI can
operate as a coherent feature instead of a partial patch.
Constraint: Desktop transcript needs to stay usable during long multi-tool sessions without hiding file-change diffs
Constraint: Permission prompts must mirror CLI behavior closely enough that read-only commands do not get desktop-only prompts
Rejected: Keep rendering Read/Bash bodies inline | too noisy and unlike the intended transcript model
Rejected: Commit only the touched desktop files | would leave the newly introduced desktop app incomplete in git history
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: Treat non-writing tools as summary-first transcript events; do not re-expand them by default without validating the UX against long sessions
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run
Tested: bun test src/server/__tests__/conversations.test.ts
Not-tested: Manual visual regression against the exact screenshots in a live desktop session
Not-tested: Full root TypeScript check (repository still has unrelated extracted-native parse failures)