Computer Use setup could fail on machines where PATH discovery misses a valid Python installation, especially Windows or conda-style environments. Store an optional interpreter path, prefer it during environment checks and venv creation, and expose a desktop settings control for selecting or clearing it.
Constraint: Python discovery is environment-specific and cannot always be inferred from PATH.
Rejected: Continue falling back to PATH after an invalid custom path | hides a user-selected broken interpreter and makes diagnosis ambiguous.
Confidence: high
Scope-risk: moderate
Directive: Preserve unknown Computer Use config fields and keep blank interpreter paths normalized to automatic detection.
Tested: bun test src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-python.test.ts src/server/__tests__/computer-use-api.test.ts
Tested: cd desktop && bun run test src/pages/ComputerUseSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Computer Use browser smoke saved /opt/homebrew/bin/python3 and backend persisted then reset pythonPath to null
Not-tested: Full bun run check:server; existing cron-scheduler-launcher test expects CLAUDE_CODE_ENTRYPOINT=sdk-cli but received undefined.
Related: https://github.com/NanmiCoder/cc-haha/issues/331
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
Online upgrades can strand users on stale desktop UI state or malformed local persistence. This adds a deny-by-default Doctor path that resets only regenerable desktop UI state, reports protected local files with redacted metadata, and keeps protected repair as a dry-run no-op until a reviewed backup-first flow exists.
Constraint: Chat transcripts, model/provider config, Skills, MCP, IM bindings, adapter sessions, OAuth tokens, plugins, and team/session records are user-owned protected state.
Rejected: Automatically rewrite malformed protected JSON | unsafe without schema-specific migrations and backups.
Rejected: Continue relying only on startup migrations | users need an explicit recovery action after a white screen.
Confidence: high
Scope-risk: moderate
Directive: Keep Doctor repair deny-by-default; do not mutate protected state without an explicit reviewed backup-first manual repair flow.
Tested: bun test src/server/__tests__/doctor-service.test.ts
Tested: cd desktop && bun run test src/components/ErrorBoundary.test.tsx src/lib/doctorRepair.test.ts src/__tests__/diagnosticsSettings.test.tsx src/components/layout/StartupErrorView.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:server
Tested: bun run verify (failed only existing agent-utils coverage baseline; changed-lines coverage 97.62%)
Not-tested: Packaged desktop manual Doctor click path.
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
Computer Use is useful when explicitly needed, but exposing its MCP tools by default creates unnecessary desktop-control surface for users who want coding-only sessions. This adds a shared disable path for CLI flags, environment, and desktop settings while keeping preauthorized app state in one config file.
The same change also preserves Windows and WSL shell startup behavior by applying the MSYS argument-conversion guard only on WSL-bound launches.
Constraint: Computer Use MCP must not be exposed to the Coding Agent when disabled
Constraint: Desktop settings and CLI sessions need to read the same persisted Computer Use config
Rejected: Environment-only disable switch | desktop users need a persistent Settings control
Rejected: Remove Computer Use setup entirely | enabled sessions still need the existing built-in MCP path
Confidence: high
Scope-risk: moderate
Directive: Keep every new Computer Use entrypoint wired through loadStoredComputerUseConfig or the CLI disable flag before adding MCP tools
Tested: bun test src/utils/computerUse/gates.test.ts src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-api.test.ts src/utils/shell/wslInterop.test.ts desktop/src/pages/ComputerUseSettings.test.tsx
Tested: bun run check:server; bun run check:desktop; bun run check:docs; bun run check:policy; bun run check:native; git diff --check
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh; codesign verify; hdiutil verify; built CLI Computer Use E2E exposure and disable checks
Not-tested: Full screenshot/control action after granting macOS Screen Recording permission on this machine
DingTalk bot unbind was routed through the generic adapter config update,
but undefined credential fields are dropped during JSON serialization. That
left stored Client ID and Secret values intact after clicking the unbind
button. Match the working WeChat path with a dedicated server endpoint and
frontend API call so credential removal is explicit.
Constraint: JSON request bodies cannot preserve undefined fields for generic config clearing
Rejected: Keep using updateConfig with undefined fields | serialized requests omit the fields before the server can clear them
Confidence: high
Scope-risk: narrow
Directive: Keep credential-clearing flows on explicit unbind endpoints rather than relying on partial config merges
Tested: bun test src/server/__tests__/adapters.test.ts
Tested: cd desktop && bun run test src/stores/adapterStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Not-tested: bun run quality:pr is blocked by repository policy requiring allow-cli-core-change approval for the broader branch impact
Bring the detached worktree terminal improvements into the local main line so desktop sessions can open a bottom terminal in the current project directory and still promote it to a full tab when needed.
Constraint: Local main already contains additional desktop fixes after the worktree base
Confidence: high
Scope-risk: moderate
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: bun run quality:pr is blocked by existing branch policy requiring allow-cli-core-change approval
Desktop terminal access should behave like an IDE: active project sessions open a bottom panel in the session working directory while keeping a full terminal tab available for dedicated use. The panel has constrained resizing and cleanup so session tab state remains isolated, and terminal guidance points users to the bundled claude-haha command for extension setup.
Constraint: Desktop bundles the user-facing CLI as claude-haha while claude-sidecar remains internal
Rejected: Always opening a standalone terminal tab | loses the current project context and diverges from common IDE behavior
Rejected: Exposing claude-sidecar in terminal guidance | it is an internal launcher, not the supportable user command
Confidence: high
Scope-risk: moderate
Directive: Keep bottom terminals keyed by session id and pass session workDir/projectPath into spawned terminals
Tested: bun run check:desktop
Tested: git diff --check
Tested: Computer Use E2E against built macOS app during implementation
Not-tested: bun run quality:pr is blocked by existing branch policy requiring allow-cli-core-change approval
The desktop composer now treats draft and not-yet-running sessions as pending
until a real CLI context snapshot exists. Context inspection has a lightweight
context-only path so the composer and /context panel do not wait on unrelated
usage or MCP status work, and desktop boot no longer blocks the first shell
render on tab restoration.
Constraint: Empty composer state has no live CLI context to inspect until a session starts
Rejected: Display 0% before live context exists | it implied one user message consumed the fixed prompt/tool baseline
Confidence: high
Scope-risk: moderate
Directive: Do not show numeric context usage without a real context snapshot
Tested: cd desktop && bun run test -- pages.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: bun run check:server
Tested: Browser reload at http://127.0.0.1:5174 with cleared open-tabs showed pending context and no /inspection request
Diagnostics exports previously preserved only compact summaries, which made user-uploaded issue bundles hard to debug. This records richer sanitized details from server, CLI, SDK, browser, React, and desktop API failure paths while keeping request bodies and secrets out of client-side reports.
Constraint: Diagnostic bundles must be useful for GitHub issues without including chat contents, file contents, full environments, or API keys.
Rejected: Only expand the Settings UI summary | the exported bundle would still miss hidden runtime failures.
Confidence: high
Scope-risk: moderate
Directive: Keep diagnostics write paths best-effort and non-blocking; do not add request-body capture without a redaction review.
Tested: bun test src/server/__tests__/diagnostics-service.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test src/api/client.test.ts src/__tests__/diagnosticsSettings.test.tsx --run
Tested: bun run check:server
Tested: bun run check:desktop
Tested: agent-browser E2E on ports 37652/41752 captured client_unhandled_rejection and exported a redacted tar.gz bundle
DingTalk uses QR registration to store client credentials, then reuses the existing IM pairing and session bridge. The merged main implementation keeps the existing WeChat QR binding path intact while adding DingTalk as a peer IM platform. The default IM workdir now falls back to the local user working directory so a newly bound chat can start immediately even when recent-project history is empty.
Constraint: Local main already carries WeChat IM, so the merge keeps WeChat config, QR binding, sidecar args, and unbind behavior intact while adding DingTalk.
Rejected: Keep empty defaultProjectDir as project-picker-only | newly bound IM users can hit a dead end with no recent projects.
Confidence: high
Scope-risk: moderate
Directive: Keep IM platform unions synchronized across config, pairing, sidecar args, desktop settings, and docs.
Tested: bun test common/ dingtalk/ wechat/; bun test src/server/__tests__/adapters.test.ts; cd adapters && bunx tsc --noEmit; bun run check:policy; bun run check:server; bun run check:desktop; bun run check:adapters; bun run check:native; bun run check:docs
Not-tested: quality:pr full gate was blocked by existing local main CLI-core diff requiring allow-cli-core-change maintainer approval; live post-fix DingTalk second-message delivery was not repeated after the merge.
WeChat needs a QR-paired path instead of bot-token setup, so the adapter layer now includes the iLink protocol calls, desktop pairing UI, server-side bind/unbind APIs, and shared IM command behavior. Empty project history falls back to the user's default work directory so mobile /new works without pre-opening a desktop project.
Constraint: Tencent iLink login returns a URL that the desktop UI must render as a QR image locally
Constraint: IM adapters should keep /new, /projects, status, permission, and default workdir behavior consistent across WeChat, Feishu, and Telegram
Rejected: Require users to paste absolute project paths for first WeChat sessions | mobile onboarding should work from the default user working directory
Confidence: high
Scope-risk: moderate
Directive: Do not change WeChat polling back to overlapping intervals; getupdates is a long-poll endpoint and must remain serialized
Tested: Real WeChat QR scan, inbound /status, outbound reply, and unbind E2E
Tested: bun run check:adapters
Tested: bun run quality:pr
Not-tested: Re-scan live WeChat after the default-workdir fallback tweak; covered by adapter config tests and PR gate
Provider presets now carry an explicit auth strategy so Anthropic-compatible services can choose the right Claude Code environment shape instead of relying on one global variable. The desktop editor exposes the same strategy, keeps settings.json previews in sync, and applies the strategy to connection tests as well as runtime env sync.
Constraint: Third-party Anthropic-compatible providers do not agree on ANTHROPIC_API_KEY versus ANTHROPIC_AUTH_TOKEN.
Rejected: Force all third-party providers onto one env var | breaks providers that require bearer auth, dual variables, or dummy local auth.
Confidence: high
Scope-risk: moderate
Directive: Add new provider presets with explicit authStrategy; do not infer auth behavior from model names alone.
Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run lint
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main
Tested: agent-browser provider edit flow on http://127.0.0.1:19811
Not-tested: Live successful calls for every third-party provider with real credentials.
Desktop failures were previously hard to debug from issue reports because server-side and CLI startup details were only partially visible in the UI. This adds a dedicated cc-haha diagnostics store with sanitized structured events, runtime error summaries, a Settings diagnostics view, and an exportable bundle that users can attach to reports.
Constraint: Diagnostic exports must not include chat content, file contents, full environment variables, API keys, bearer tokens, cookies, or OAuth tokens.
Rejected: Export raw server logs | easier to debug but too likely to leak secrets and private workspace data.
Rejected: Keep diagnostics only in transient UI errors | still leaves maintainers unable to diagnose later GitHub issues.
Confidence: high
Scope-risk: moderate
Directive: Do not add raw transcript, prompt, attachment, or environment dumps to diagnostics without a separate privacy review.
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/diagnostics-service.test.ts
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversation-service.test.ts
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run test src/__tests__/diagnosticsSettings.test.tsx --run
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: agent-browser E2E on local server/dev UI for CLI startup failure, provider test failure, diagnostics tab, copy summary, export bundle, and tar/secret scan
Not-tested: Destructive clear-logs button in browser E2E; local deletion was intentionally not clicked.
The chat timeline lost per-turn changed file cards when the visible user message ids did not match persisted transcript ids, and sessions without SDK file-history snapshots had no fallback source for turn changes. Match live cards by stable user-message index when needed and derive checkpoint previews from transcript tool calls when snapshots are absent.
Constraint: Right-side workspace changes already derive from transcript tool calls, so chat checkpoint data must remain consistent with that source.
Rejected: Force a history reload after every turn | would hide the id mismatch but add churn and still not cover transcript-only sessions.
Confidence: high
Scope-risk: moderate
Directive: Keep snapshot checkpoints as the preferred source, and use transcript extraction only as the compatibility fallback.
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: bun test src/server/__tests__/workspace-service.test.ts
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test
Tested: git diff --check
Not-tested: Browser E2E against a live model session.
The desktop workspace panel and chat transcript were mixing project-level file state with per-turn session history, which made rewind, file attachment, and changed-file previews brittle across multi-turn and historical sessions. This keeps turn checkpoints durable in the transcript, makes workspace refreshes happen at the right lifecycle points, and hardens long file previews without blocking the UI.
Constraint: Right-side workspace changes are project working-tree state, while chat turn cards are session checkpoint state.
Rejected: Treat every changed-file panel entry as session-local | new sessions must still reveal existing dirty project files.
Confidence: high
Scope-risk: moderate
Directive: Do not couple global workspace status to session checkpoint cards without preserving both product meanings.
Tested: cd desktop && bun run test src/components/workspace/WorkspacePanel.test.tsx
Tested: cd desktop && bun run lint
Tested: git diff --check
Tested: cd desktop && bun run test
Not-tested: Packaged Tauri runtime smoke test
Workspace file preview is a local desktop surface, so image files should not be blocked by the text preview byte cap. Large text files now return a bounded preview instead of an unusable too-large state, while binary files remain explicitly unsupported.
Constraint: Avoid unbounded text payloads that can freeze the desktop renderer
Rejected: Remove all read limits | large generated files can still overload JSON transport and syntax rendering
Confidence: high
Scope-risk: moderate
Directive: Keep image preview detection before text-size limiting so local images remain renderable
Tested: bun test src/server/__tests__/workspace-service.test.ts
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/components/workspace/WorkspacePanel.test.tsx
Tested: cd desktop && bun run build
Not-tested: Full packaged macOS smoke after 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
Session inspection was coupling quick status and usage rendering to live context control requests, so a slow get_context_usage path could leave the desktop inspector stuck on a loading state. The desktop panel now loads basic inspection data first, renders a transcript-based context estimate immediately, and only asks for live context details as a background refinement.
The translation hook now returns a stable function per locale so effects that depend on translation do not reset and re-fetch after every render.
Constraint: Third-party provider sessions may not expose reliable live context capabilities through the control request path.
Rejected: Keep waiting on live get_context_usage for the context tab | it recreates the user-visible loading stall.
Rejected: Hardcode provider-specific context windows | provider capabilities are not always known from the desktop session record.
Confidence: high
Scope-risk: moderate
Directive: Do not make the inspector first render depend on live CLI control requests without a transcript or cached fallback.
Tested: bun test src/server/__tests__/conversations.test.ts -t "structured session inspection|Sonnet 4.6 transcript usage"
Tested: cd desktop && bun run test -- --run src/i18n/index.test.tsx
Tested: cd desktop && bun run build
Tested: git diff --check
Tested: agent-browser verified /context renders transcript estimate without Loading context data
Not-tested: Provider-specific true context-window discovery for every third-party vendor
Desktop slash commands now separate local UI panels from CLI turn execution. The session inspector exposes status, usage, and context data from the active session, including transcript and context fallbacks, so /status, /cost, and /context can render structured desktop UI instead of raw terminal text. The inspector and help surfaces now use the existing desktop i18n catalogs for English and Chinese labels.
Constraint: Desktop read-only slash commands must not spawn duplicate CLI processes or depend on submitting a normal user turn.
Rejected: Render raw CLI command text in chat | it keeps terminal-specific layout constraints and does not fit the desktop panel UX.
Confidence: high
Scope-risk: moderate
Directive: Keep /status, /cost, and /context routed through the local inspector unless the CLI exposes a structured interactive command protocol.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun test src/components/chat/composerUtils.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run build
Tested: agent-browser smoke test on http://127.0.0.1:2024/ for /context localized inspector
Tested: git diff --check
Not-tested: Full packaged Tauri desktop build.
Desktop rewind previously used the visible user-message index as the primary selector. That can drift from the persisted active chain when hidden or non-rendered user messages exist, so the API now prefers a stable user message id and checks the selected prompt text before mutating transcript or files.
Constraint: Desktop UI can hide transcript entries that still exist in the persisted session chain
Rejected: Keep index-only rewind | can target an earlier user message after UI/server chain drift
Confidence: high
Scope-risk: moderate
Directive: Do not remove targetUserMessageId or expectedContent guards without reproducing shifted visible-index sessions
Tested: bun test src/server/__tests__/sessions.test.ts --timeout 20000
Tested: cd desktop && bun run test MessageList.test.tsx chatStore.test.ts -- --run
Tested: desktop/scripts/e2e-rewind-agent-browser.sh
Tested: desktop/scripts/e2e-rewind-complex-agent-browser.sh
Not-tested: Full desktop Vitest suite still has unrelated locale-sensitive failures
The detached worktree implementation adds a Settings terminal for command-based setup flows. The merge keeps the target branch's current adapter/runtime dependencies while adding the new portable-pty backend, xterm frontend, UTF-8 output handling, and host-shell environment propagation.
Constraint: Target branch had newer desktop dependency changes, including anyhow in the Tauri crate.
Constraint: Existing uncommitted work in the main worktree had to stay out of this merge commit.
Rejected: Fast-forward the checked-out branch under a dirty worktree | would risk mixing unrelated local changes into the branch state.
Confidence: high
Scope-risk: moderate
Directive: Treat the terminal as host-shell integration; keep PATH and UTF-8 locale handling together when changing startup behavior.
Tested: cargo fmt --check
Tested: cargo test --lib
Tested: cargo check
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test src/pages/TerminalSettings.test.tsx
Tested: git diff --check
Not-tested: Rebuilt macOS DMG from the target branch after merge.
Some setup flows end in a shell command instead of a natural-language install path, so Settings now exposes a host PTY terminal backed by portable-pty and xterm. The terminal inherits the user's login-shell environment, forces a UTF-8 locale when needed, and preserves split UTF-8 output so Chinese paths render correctly.
Constraint: Desktop GUI apps do not inherit the user's interactive shell PATH on macOS.
Constraint: Command output may split UTF-8 characters across PTY reads.
Rejected: Use Tauri shell commands only | users need an interactive PTY for copy-pasted install flows.
Rejected: Ask users to edit shell profiles | terminal setup should work out of the box.
Confidence: high
Scope-risk: moderate
Directive: Keep terminal startup tied to host environment checks; do not bundle runtimes to solve PATH issues.
Tested: cargo fmt --check; cargo test --lib; cargo check; cd desktop && bun run lint; cd desktop && bun run test src/pages/TerminalSettings.test.tsx; cd desktop && bun run build; cd desktop && bun run build:macos-arm64
Tested: Computer Use verified npm is available in the built macOS app terminal and Chinese output renders after UTF-8 decoding fix.
Not-tested: Native Windows/Linux package runtime validation.
The desktop app already shipped a bundled sidecar, but only desktop-managed
sessions could see it. This change installs a `claude-haha` launcher into the
user bin directory, wires PATH setup so new terminals can resolve it, and keeps
desktop installer sessions aligned on the same bundled sidecar resolution path.
The desktop install surface now also reports whether the launcher is ready or
still waiting on a terminal restart.
Constraint: The worktree already contains unrelated icon, docs, and UI changes, so this commit stages only the bundled CLI launcher slice
Rejected: Tell users to install the official Claude CLI separately | it breaks the desktop out-of-box install story
Rejected: Keep the bundled CLI reachable only inside desktop-managed shells | system terminals would still be unable to call the packaged runtime
Rejected: Symlink directly into the app bundle instead of copying to user bin | moving or replacing the app bundle would leave a stale launcher behind
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Proxy-backed non-Anthropic providers still depend on the desktop server; do not assume this launcher makes every provider fully standalone
Tested: bun test src/server/__tests__/desktop-cli-launcher.test.ts src/server/__tests__/settings.test.ts; bun test src/server/__tests__/conversation-service.test.ts; bun test src/utils/shell/bashProvider.test.ts; cd desktop && bun x vitest run sidecars/launcherRouting.test.ts src/components/settings/InstallCenter.test.tsx; cd desktop && bun run lint; cd desktop && bun run build; cargo check --manifest-path desktop/src-tauri/Cargo.toml
Not-tested: Manual packaged desktop app install plus real Terminal/iTerm/PowerShell invocation on fresh macOS and Windows machines
The desktop Settings flow needed a real shell for bundled CLI setup, but the
restart path could hang behind old PTY teardown. Wire an xterm.js panel to a
portable-pty backend, inject the bundled CLI into the shell bootstrap, and
switch to the new session before cleaning up the previous one so restart work
stays off the frontend critical path.
Constraint: The desktop app must ship its own CLI entrypoint instead of depending on a global Claude install
Constraint: PTY teardown must not block the Tauri invoke path
Rejected: Reuse the install chat for arbitrary shell commands | it does not provide a real interactive PTY
Rejected: Close the old PTY before adopting the new session | it keeps restart vulnerable to hung child shutdown
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep PTY teardown off the invoke path and preserve session handoff ordering when changing terminal lifecycle code
Tested: bunx vitest run src/__tests__/terminalPanel.test.tsx src/components/settings/TerminalPanel.restart.test.tsx; bun run lint; cargo check
Not-tested: End-to-end command echo inside the packaged desktop terminal still needs follow-up runtime verification
The desktop provider flow was still mixing provider-managed state with
`~/.claude/settings.json`, which let unrelated tools leak fields like
`ANTHROPIC_REASONING_MODEL` and `model` back into the active provider path.
This change moves the provider JSON editor onto `~/.claude/cc-haha/settings.json`,
routes provider settings through dedicated `/api/providers/settings` endpoints,
and makes model reads/writes under an active provider use the managed cc-haha
settings instead of the global user settings file.
Constraint: Active provider model selection must be isolated from legacy ~/.claude/settings.json
Rejected: Keep merging provider JSON with settingsApi.getUser() | external tools can reintroduce unrelated model fields into the provider flow
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future provider-model UI or runtime change should read/write cc-haha managed settings first, not the global user settings file
Tested: bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/settings.test.ts; cd desktop && bun run lint
Not-tested: Manual desktop provider modal interaction after the cc-haha settings API switch
The provider picker had two separate preset definitions and some defaults had
started to drift from the model IDs vendors document for Claude Code /
Anthropic-compatible usage. This change moves built-in presets to one
server-side JSON source, serves that source through the existing presets API,
and makes the desktop settings page consume the API instead of a duplicated
frontend constant. The preset defaults were then corrected against vendor docs
so the built-in values match documented model IDs and casing.
Constraint: Built-in provider defaults must match official vendor Claude Code or Anthropic docs
Rejected: Keep duplicated frontend and backend preset lists | values and casing drifted independently
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Update src/server/config/providerPresets.json and rerun provider-presets tests before changing built-in provider defaults again
Tested: bun test src/server/__tests__/provider-presets.test.ts; cd desktop && bun run lint
Not-tested: Manual desktop settings UI interaction after fetching presets from the API
Desktop MCP settings was synchronously probing every configured server from /api/mcp, which made the list page scale with connection latency and surface brittle behavior as installs accumulated more MCPs. Return lightweight snapshot rows from the list API, keep explicit status checks separate, and let the desktop UI refresh status in a constrained background lane while preserving project-aware server identity.
Constraint: MCP list must stay responsive even with many configured servers
Rejected: Probe all servers from the list view without limits | still fans out with server count and can overload slow installs
Rejected: Keep servers permanently unchecked until detail view | misses the desired loading feedback when the MCP page opens
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep /api/mcp as a configuration snapshot endpoint; do not reintroduce per-row live connect work on list load
Tested: bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Real desktop interaction against an environment with dozens of live MCP servers
Merged the desktop MCP management work into local main and retained
existing plugin settings affordances while resolving router and settings-tab
conflicts. The resulting flow keeps MCP settings global-only for speed and
uses slash-command cards to route users into concrete MCP or skill targets.
Constraint: local main already had plugin settings and API routes that had to remain available
Rejected: Favor the worktree version wholesale during merge | would have dropped local plugin tab and router support on main
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep slash-command entry flows and settings tabs additive during future merges; do not collapse MCP and plugin navigation into one another
Tested: Conflict resolution review during merge; source commit 66edded validated with bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- pages.test.tsx mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Re-running full desktop/manual verification from the merged main worktree after merge completion
Related: 66edded
Desktop MCP management now has a working server API, a global-only settings surface,
and slash-command entry points that surface MCP and skills from the composer before
routing users into the right settings view.
Constraint: Project-scoped MCP browsing in settings was too slow and noisy because it scanned multiple workdirs
Rejected: Keep project MCP aggregation on the settings homepage | duplicated entries and poor responsiveness
Rejected: Route /mcp directly on Enter without an intermediate card | removed the user's ability to choose a specific target first
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep settings focused on global MCP; add project-scoped MCP affordances in the chat-context slash surfaces instead of re-expanding the settings homepage
Tested: bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- pages.test.tsx mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Manual IAB verification after this final commit/merge cycle
The desktop app could read plugin-produced skills and agents, but it had no
plugin control plane of its own. This adds a dedicated Settings tab backed by
server-side plugin APIs so installed plugins can be inspected, enabled,
disabled, updated, reloaded, and uninstalled from the WebUI.
The implementation also teaches browser-based desktop dev sessions to honor a
custom backend URL, which made it possible to run isolated worktree ports for
real UI automation. During verification, the long-lived desktop server kept a
stale installed-plugin snapshot after external CLI mutations, so cache clearing
now resets that session-level plugin installation state as well.
Constraint: Desktop WebUI needed an isolated backend URL instead of the hard-coded 127.0.0.1:3456 fallback
Constraint: Reuse existing plugin operations and loaders instead of rebuilding plugin lifecycle logic in the desktop layer
Rejected: Fold plugin management into Skills or Adapters | mixed unrelated lifecycles and hid plugin-specific health/actions
Rejected: Expose only read-only plugin status in desktop | did not satisfy enable-disable-reload-uninstall verification needs
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop plugin actions routed through the shared plugin operation layer and clear installed-plugin session caches when plugin state changes externally
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/__tests__/pluginsSettings.test.tsx
Tested: bun test src/server/__tests__/plugins.test.ts src/server/__tests__/skills.test.ts
Tested: Browser automation against isolated ports 15120/38456 covering discord plugin list/detail/disable/apply/enable/update/uninstall flows
Not-tested: Full desktop session runtime parity with CLI /reload-plugins AppState refresh beyond the new desktop API path
The desktop client already had a conversation-level rewind UI concept on the
CLI side, but the web/desktop surface lacked the protocol, session trimming,
and file checkpoint restore path needed to make rewind trustworthy. This change
adds a desktop-specific rewind API, wires the message-level UI affordance and
confirmation modal, enables SDK file checkpointing for desktop sessions, and
covers the restore path with service tests plus a real agent-browser workflow.
Constraint: Desktop sessions run the CLI in SDK/print mode, so file checkpointing had to be enabled explicitly for that path
Constraint: main branch is checked out in a separate worktree, so merge-back must happen from the primary worktree after commit
Rejected: UI-only rewind that only trims local state | would leave persisted transcript and disk state inconsistent after refresh
Rejected: Reuse getLastSessionLog as the sole snapshot source | active rewind must read file-history metadata directly from the session file
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop rewind keyed to persisted user-message order unless the UI model starts carrying stable transcript UUIDs end-to-end
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/sessions.test.ts; desktop MessageList vitest; desktop tsc no-emit; live agent-browser E2E on isolated ports with file edit then rewind
Not-tested: Browser E2E matrix for multi-file and second-edit scenarios is still covered at service-test level rather than full UI level
Desktop session interruptions were leaving two different classes of stale UI
artifacts behind: reconnects could silently lose in-flight output, and history
reloads could replay synthetic interruption or internal command breadcrumbs as
if they were user-facing transcript content. This change rebinds active session
output to the latest client websocket on reconnect, preserves queued outbound
messages across transient disconnects, and filters synthetic transcript entries
before they reach the desktop history API so reloads reconstruct only the
messages users should actually see.
Constraint: Desktop transcript history is shared with CLI JSONL files and must stay compatible with persisted message shapes
Rejected: Hide these artifacts only in React renderers | other transcript consumers would still receive polluted history
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep transcript filtering aligned with real synthetic/internal message shapes before adding new hidden message types
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd /Users/nanmi/.codex/worktrees/e7ac/claude-code-haha/desktop && bun run test -- websocket.test.ts
Tested: cd /Users/nanmi/.codex/worktrees/e7ac/claude-code-haha/desktop && bun run lint
Not-tested: Manual browser verification of the refreshed session transcript after interrupt on this exact session
Completed desktop task bars were only being dismissed locally, which let the
persisted task list resurface on refresh or bleed into the next user turn.
This wires the existing server-side reset path into the desktop stores and
session flow so a finished task cycle is summarized once, cleared locally,
and removed remotely before the next round starts.
Constraint: Existing task persistence already lives behind the server task-list API and must stay compatible with persisted JSON task files
Rejected: Only hide the completed task bar in UI state | left stale persisted tasks behind and reintroduced them on reload
Rejected: Clear desktop state without a task summary | dropped useful completion context from the chat transcript
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop task dismissal and task-list persistence behavior aligned; do not reintroduce local-only clearing without covering reload and next-turn flows
Tested: `cd desktop && bun run lint`
Tested: `cd desktop && bun run test src/stores/cliTaskStore.test.ts src/stores/chatStore.test.ts src/components/chat/SessionTaskBar.test.tsx src/components/chat/ComputerUsePermissionModal.test.tsx`
Tested: `bun test src/server/__tests__/e2e/business-flow.test.ts --test-name-pattern "Task Lists API"`
Not-tested: Full `bun test src/server/__tests__/e2e/business-flow.test.ts` suite still has unrelated pre-existing failures in Models and Sessions sections
The sidebar project filter had regressed to lossy projectPath strings, so the
UI showed broken folder-name fragments and the dropdown could not match the
richer project chooser used in session creation. This switches the sidebar back
to recent-project metadata, renders the filter menu through a portal, and keeps
session filtering behavior unchanged while restoring readable labels and paths.
Constraint: Sidebar filtering still needs to target existing session projectPath values without migrating store shape
Constraint: The desktop sidebar dropdown must escape local stacking and overflow constraints to render reliably
Rejected: Keep deriving labels from sanitized projectPath strings | lost repo names and full paths, causing misleading project choices
Rejected: Rebuild sessionStore around recent-project objects in this fix | broader state migration than needed for the regression
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep sidebar project displays aligned with /api/sessions/recent-projects metadata instead of reintroducing string-splitting fallbacks
Tested: cd desktop && bunx vitest run src/components/layout/ProjectFilter.test.tsx src/components/layout/Sidebar.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Manual desktop visual pass of the repaired dropdown in a live Tauri window
The desktop app previously lost project-level skills in two places: the
settings browser only queried user skills, and the slash-command picker for a
fresh session depended on CLI init state that does not exist before the first
turn. This change makes the skills APIs cwd-aware, falls back to project skill
loading before CLI init, syncs sidebar session deletion with open tabs, and
aligns the empty-session composer styling so the pre-message session UI stays
consistent.
Constraint: Fresh desktop sessions need slash-command discovery before the CLI websocket emits system/init
Constraint: Only repository code should be committed; build artifacts, installs, and /tmp smoke fixtures stay out of git
Rejected: Rebuild slash-command listings from full getCommands() in the sessions API | introduced auth-gated command dependencies unrelated to local skill discovery
Rejected: Unify EmptySession and ChatInput into one full component now | higher regression risk for normal chat interactions than a visual-only hero variant
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep pre-message skill discovery keyed to session workDir so project-level .claude/skills remain visible before the first turn
Tested: bun test src/server/__tests__/sessions.test.ts; cd desktop && bun run test -- --run src/__tests__/skillsSettings.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx src/pages/ActiveSession.test.tsx; cd desktop && bun run lint
Not-tested: Manual desktop smoke test against the rebuilt .app bundle in the local GUI
This bundles the pending desktop/server team-session fixes with the local adapter recovery changes already in the worktree. The team path now keeps teammate membership stable under concurrent spawns, surfaces real teammate identities in the desktop UI, and allows direct interaction with member transcripts. The adapter changes recover automatically when stale thinking signatures invalidate an existing session.
Constraint: Team config writes can happen concurrently while multiple reviewers spawn in parallel
Constraint: Desktop member views must follow mailbox/transcript semantics rather than hijacking teammate runtime sessions
Rejected: Keep relying on config.json alone for member discovery | in-process teammates can be lost after concurrent writes
Rejected: Open teammate sessionIds as normal desktop sessions | would attach a second CLI instead of the running teammate
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Preserve locked team-file mutation for any future teammate registration path and keep teammate labels sourced from member names before agent types
Tested: bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts
Tested: cd desktop && bun run test --run src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Manual end-to-end validation against a live Agent Teams run in the desktop app
- 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>
Add a protocol-translating reverse proxy that allows using OpenAI-compatible
API providers (DeepSeek, OpenRouter, Groq, etc.) with Claude Code.
The proxy intercepts Anthropic Messages API requests from the CLI, transforms
them to OpenAI Chat Completions or Responses API format, forwards to the
upstream provider, and transforms streaming/non-streaming responses back.
Key features:
- Request transform: Anthropic Messages → OpenAI Chat/Responses
- Response transform: OpenAI → Anthropic (streaming SSE + non-streaming)
- Provider-agnostic reasoning support (reasoning_content, thinking_blocks,
reasoning fields from DeepSeek, OpenAI o-series, GLM-5, Groq, etc.)
- Event queue pattern for correct Anthropic SSE event ordering
- Two-step test: ① connectivity check ② full proxy pipeline validation
- Desktop UI: API format selector, two-step test results display
- License attribution for cc-switch (MIT, Jason Young)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update AgentDefinition type with agentType, source, isActive, modelDisplay fields
- Refactor agentStore to support activeAgents/allAgents with cwd parameter
- Add i18n strings for agent browser UI (source labels, summary, status)
- Update server agents API with serialization helpers and override resolution
- Update tests to match new agent data structure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Server: GET /api/skills (list) and GET /api/skills/detail (tree + files)
- Desktop: Skills tab with grouped list, file tree navigation, Markdown/code preview
- i18n: EN/ZH support
- Types, API client, Zustand store
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>