The contributor PR adds useful Windows terminal and portable mode support, but it also reintroduced an older General settings zoom block and left the new native settings paths without enough regression coverage. This commit keeps the feature direction intact while removing the duplicate UI, making invalid bash paths fail at save time, and covering the portable cache and app-mode paths with focused tests.
Constraint: This commit lands directly on the contributor PR branch to avoid a long review-comment loop.
Rejected: Ask the contributor to rework the PR from scratch | the remaining issues are narrow and maintainable by us.
Confidence: high
Scope-risk: moderate
Directive: Keep future portable-mode changes covered at the native boundary and the desktop store boundary.
Tested: cd desktop && bun run test src/pages/TerminalSettings.test.tsx src/__tests__/generalSettings.test.tsx src/stores/settingsStore.test.ts
Tested: bun test src/utils/__tests__/cachePaths.test.ts
Tested: cd desktop/src-tauri && cargo test
Tested: cd desktop && bun run lint
Tested: cd desktop/src-tauri && cargo check
Tested: bun run check:server
Tested: bun run check:desktop
Not-tested: Manual Windows packaged-app portable-mode smoke; to be covered before a future release.
Desktop plugin enable and disable actions are written by the server process, while an already-running CLI owns its own settings cache. Reloading only the plugin caches left enabledPlugins stale after a disable-then-enable cycle, so the slash command list stayed empty until a new conversation spawned a fresh CLI.
Reset the settings cache at the active plugin refresh boundary before resolving enabled plugins. The regression test simulates an external desktop enable toggle after a cached disabled read and verifies the plugin slash command is restored in the same refresh.
Constraint: Desktop server and active CLI are separate processes with independent in-memory settings caches
Rejected: Restart the CLI on every plugin toggle | heavier user-visible lifecycle change and unnecessary once the reload handoff invalidates settings
Confidence: high
Scope-risk: narrow
Directive: Keep reload_plugins as the handoff that rereads settings before plugin command discovery
Tested: bun test src/server/__tests__/plugins.test.ts
Tested: bun run check:server
Desktop sessions must treat the global Thinking toggle as the source of truth across active CLI sessions, title generation, and provider-compatible request bodies. DeepSeek-style streams can start with reasoning blocks before text, so the WebSocket bridge now keeps the UI in thinking state until text content actually starts.
Constraint: DeepSeek and MiniMax use Anthropic-compatible surfaces but expose thinking behavior through provider-specific request and stream shapes
Rejected: Keep provider-specific disabled-thinking env defaults | stale defaults kept overriding the user's global Thinking setting
Rejected: Treat every non-tool stream block as text | reasoning-only startup blocks created a blank streaming gap before the visible thinking bubble
Confidence: high
Scope-risk: moderate
Directive: Do not change thinking stream state transitions without replaying reasoning-before-text provider events
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: bun run check:server
Tested: bun run verify
Not-tested: Live DeepSeek provider smoke with real quota
This merge carries the worktree fix into the local main branch so long multi-session desktop usage keeps rendering cost bounded while DeepSeek follows the shared General Settings Thinking toggle. It preserves running-session behavior and keeps legacy DeepSeek managed env from pinning thinking off after upgrade.
Constraint: User requested the worktree result merged into local main without pushing.
Confidence: high
Scope-risk: moderate
Directive: Keep DeepSeek controlled by the global Thinking setting; do not restore provider-specific disabled-thinking defaults without both toggle-state tests.
Tested: bun test src/server/__tests__/conversations.test.ts -t "global Thinking setting control DeepSeek"
Tested: bun test src/server/__tests__/persistence-upgrade.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/title-service.test.ts src/utils/__tests__/thinking.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts
Tested: cd desktop && bun run test -- MessageList.test.tsx ContextUsageIndicator.test.tsx
Tested: bun run check:server
Virtualized chat rendering keeps inactive long-running sessions cheap without disconnecting their CLI process, while DeepSeek now relies on the shared Thinking setting instead of a provider-specific disabled-thinking override. Existing legacy DeepSeek managed env is normalized so old local settings do not keep suppressing reasoning output after upgrade.
Constraint: Multiple desktop tabs must keep live sessions running and remain quick to switch without reconnecting.
Rejected: Pause or disconnect hidden sessions | would delay tab switching and interrupt streaming/tool state visibility.
Rejected: Keep DeepSeek disabled-thinking preset | conflicts with the General Settings Thinking control.
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce provider-specific disabled-thinking defaults for DeepSeek without testing both General Settings toggle states.
Tested: bun test src/server/__tests__/conversations.test.ts -t "global Thinking setting control DeepSeek"
Tested: bun test src/server/__tests__/persistence-upgrade.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/title-service.test.ts src/utils/__tests__/thinking.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts
Tested: cd desktop && bun run test -- MessageList.test.tsx ContextUsageIndicator.test.tsx
Tested: bun run check:server
Desktop startup can fail before React mounts on older WebViews, so an HTML-level watchdog now renders startup diagnostics even when the module bundle never reaches the app code. Persistent provider migration now imports legacy root provider config into cc-haha-owned storage without deleting the old source file, and plugin marketplace cleanup refuses obvious corrupted cache roots or outside paths.
Constraint: User explicitly accepted the current reviewed state for landing despite remaining review concerns.
Constraint: Global ~/.claude state is user-owned and protected; automatic repair must avoid deleting shared config, transcripts, skills, MCP, plugins, OAuth, adapters, and teams.
Rejected: Tell users to delete ~/.claude or ~/.claude/cc-haha | unsafe because it can destroy user-owned Claude state and still may not fix WebView compatibility failures.
Confidence: medium
Scope-risk: moderate
Directive: Do not weaken protected-path checks; future deletion paths should validate real paths and symlink behavior before recursive rm.
Tested: bun test src/utils/plugins/installedPluginsManager.test.ts src/utils/plugins/marketplaceManager.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- --run src/main.test.tsx index-html.test.ts vite-config.test.ts src/theme/globals.test.ts
Tested: cd desktop && bun run build
Tested: bun run check:coverage
Not-tested: live macOS 12/Safari 15 WKWebView startup on an affected machine.
Not-tested: H5 diagnostic URL redaction and symlink-escape hardening are known follow-up risks from review.
Settings reported false negatives for resources the runtime could load, because the API paths used narrower discovery rules than the execution path. This keeps the Settings surfaces read-only where appropriate while matching runtime visibility for linked skills, version-constrained plugins, and merged MCP sources.
Constraint: Settings MCP list must not actively connect to servers while loading.
Rejected: Add UI-specific fallbacks | would preserve drift between Settings and runtime loaders
Confidence: high
Scope-risk: moderate
Directive: Keep Settings resource listing backed by the same shared loader semantics as runtime discovery.
Tested: bun test src/server/__tests__/skills.test.ts
Tested: bun test src/server/__tests__/plugins.test.ts
Tested: bun test src/server/__tests__/mcp.test.ts
Tested: bun run check:server
Tested: bun run check:coverage
Tested: bun run verify
Not-tested: Live external MCP connector account state
Providers like DeepSeek with 1M context windows can report
input_tokens that, when combined with output_tokens, exceed the
declared context window — causing the UI to show >100% usage.
Add an optional contextWindow parameter to calculateCurrentContextTokenTotal
that clamps the result to the window size. Pass it from analyzeContext
and sessionService where the window size is already available.
Backward compatible: callers that omit the parameter keep existing behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Integrate the Token usage settings page on top of the newer local main branch, preserving the existing H5 access router surface while adding the activity stats API route.
Constraint: Local main already contained H5 access router changes after the Token usage worktree base.
Rejected: Prefer either router branch during conflict resolution | both API surfaces are independent and must remain registered.
Confidence: high
Scope-risk: narrow
Directive: Keep h5-access and activity-stats routes registered together in src/server/router.ts.
Tested: Conflict marker scan for src/server/router.ts
Not-tested: Post-merge desktop/server checks pending
Expose local Claude Code CLI transcript usage in Settings so users can inspect recent token consumption and daily activity without leaving the desktop app.
The page uses server-side transcript aggregation for session, message, tool, model-token, and subagent token data. Daily token buckets use assistant message timestamps, and daily session counts use active parent sessions for the same date bucket so resumed sessions and cross-midnight work do not produce token-only days. Cache accounting is bumped to v5 to force recomputation under the corrected daily semantics.
Constraint: Usage data must come from local Claude Code CLI transcripts rather than mock/demo data.
Constraint: Desktop navigation keeps Token usage directly above Diagnostics.
Rejected: Bucket all token usage by session start date | hides resumed-session and cross-midnight consumption from the actual day it was spent.
Confidence: high
Scope-risk: moderate
Directive: Keep daily token and daily session counts on the same date-bucketing semantics.
Tested: bun run check:desktop
Tested: bun run check:server
Tested: Browser verification for Token usage in English and Chinese locale date labels
Not-tested: Full bun run verify quality gate
Some Anthropic-compatible providers can return successful responses with
missing or partial usage metadata, especially after falling back to
non-streaming requests. Normalize usage at the provider response boundary so
transcript writes, side queries, and cost tracking continue without treating
token accounting as required response content.
Constraint: Third-party Anthropic-compatible providers do not always return complete usage fields.
Rejected: Add provider-specific handling | this is a protocol compatibility issue across proxies and gateways
Confidence: high
Scope-risk: moderate
Directive: Keep provider response normalization vendor-neutral; do not branch on provider names for missing usage fields.
Tested: bun test src/services/api/emptyUsage.test.ts src/utils/__tests__/thinking.test.ts src/server/__tests__/title-service.test.ts src/server/__tests__/conversations.test.ts
Tested: bun run check:server
Tested: bun run check:persistence-upgrade
Tested: git diff --check
Not-tested: Live Shengsuan Cloud provider replay
Bring the detached worktree fix onto local main after verifying the title-generation regression path and local quality checks.
Constraint: The worktree commit was based on an older main commit, while local main already contained later chat scroll fixes.
Confidence: high
Scope-risk: moderate
Directive: Keep title sanitation shared across CLI, server, and desktop read paths.
Tested: bun run check:server
Tested: bun run check:desktop
Tested: bun run verify (7 passed, 1 failed on existing aggregate agent-utils coverage baseline)
Not-tested: live model title-generation smoke
Slash-command and skill prompts can enter the title-generation path as internal XML breadcrumbs. The async title request previously treated that transport metadata as user prose, so a generated ai-title could persist raw command tags and override the normal session title.
This routes title sources and generated title output through a shared sanitizer, preserving user-visible command names and arguments while dropping unrelated internal XML metadata. Desktop fallback title rendering now applies the same cleanup so existing transcripts with bad ai-title entries recover on read.
Constraint: Session titles can be produced by both desktop server logic and the SDK generate_session_title control request.
Rejected: Only clean desktop session display | leaves CLI and remote title generation able to persist bad titles again
Confidence: high
Scope-risk: moderate
Directive: Keep generated-title input and persisted-title readback on the shared sanitizer; do not add a title path that reads command XML directly.
Tested: bun test src/utils/__tests__/sessionTitle.test.ts src/utils/__tests__/sessionTitleText.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
Tested: bun run verify (7 passed, 1 failed on existing aggregate agent-utils coverage baseline)
Not-tested: live model title-generation smoke
Bring the detached worktree fix into the local main line while preserving the newer main behavior that passes session context into interactive chat message blocks.
Constraint: Local main already contains six unpublished commits on top of origin/main
Constraint: Merge conflict only affected MessageList message rendering around sessionId propagation
Rejected: Overwrite main's MessageBlock call shape | would regress AskUserQuestion and permission interactions that need sessionId
Confidence: high
Scope-risk: moderate
Directive: Keep MessageBlock sessionId propagation when editing chat rendering; AskUserQuestion depends on it
Tested: cd desktop && bun run test -- MessageList.test.tsx
Not-tested: Full verify after merge; previous verify remains blocked by agent-utils global coverage baseline
Chat sessions need predictable navigation when users switch tabs or read history during streaming. This preserves each session's scroll position, defaults fresh sessions to the latest message, and adds a compact jump-to-latest affordance when auto-follow is paused.
The PR gate also exposed that provider-scoped scheduled tasks must force the sdk-cli entrypoint when launched through the sidecar, so the same change records that runtime contract and its regression assertion.
Constraint: Desktop chat should not force-scroll while the user is reading older messages
Constraint: Scheduled task provider env must not inherit stale parent model runtime values
Rejected: Persist scroll positions in localStorage | session scroll is transient UI state and should not survive app restarts
Confidence: high
Scope-risk: moderate
Directive: Do not remove the sdk-cli entrypoint marker from provider-scoped cron tasks without rerunning the sidecar launcher regression
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/cron-scheduler-launcher.test.ts src/utils/__tests__/themeWords.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Tested: bun run check:native
Not-tested: bun run verify still fails on pre-existing agent-utils global coverage baseline below threshold
Not-tested: Chrome extension E2E blocked by Codex Chrome Extension communication timeout after plugin diagnostics passed
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
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
Scheduled tasks used the model selector in model-only mode, so a task could store a third-party model id without the provider runtime needed to execute it. The desktop task editor now stores the selected provider with the model, and the scheduler injects the same provider-scoped runtime env that desktop sessions use before launching the sidecar.
Constraint: Scheduled tasks run outside an active desktop websocket session, so provider runtime must be serialized on the task itself.
Rejected: Reuse session runtime localStorage for tasks | scheduled executions can run long after the UI session is gone.
Confidence: high
Scope-risk: moderate
Directive: Keep task execution provider env isolated from inherited process env; stale parent ANTHROPIC_* values must not override task provider choices.
Tested: cd desktop && bun run test -- src/components/tasks/NewTaskModal.test.tsx src/components/controls/ModelSelector.test.tsx
Tested: bun test src/server/__tests__/scheduled-tasks.test.ts src/server/__tests__/cron-scheduler-launcher.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Tested: git diff --check
Not-tested: Live third-party provider API call; covered by sidecar launch integration with provider proxy env.
Contributors and coding agents need one local command that both reports and enforces the quality contract. This change turns the PR gate into the shared verification entrypoint, adds path-selected local lanes, tightens coverage accounting around changed lines, and documents the repair loop in contributor and agent-facing guidance.
Constraint: Ordinary PR verification must stay non-live and runnable without provider credentials
Constraint: Coverage policy updates in this commit require maintainer approval before push/merge
Rejected: Keep quality guidance only in docs | agents need executable scripts and AGENTS.md instructions to follow the loop consistently
Confidence: high
Scope-risk: broad
Directive: Do not bypass `bun run verify` for production changes; fix failed lanes and coverage reports instead of lowering thresholds
Tested: bun run check:policy
Tested: ALLOW_CLI_CORE_CHANGE=1 ALLOW_COVERAGE_BASELINE_CHANGE=1 bun run verify
Not-tested: live provider baseline; no provider credentials were required for this non-live PR gate
The repository now has a measurable PR quality path instead of a loose set of
manual checks. Coverage, quarantine governance, provider smoke, desktop smoke,
and workflow wiring all produce durable reports that contributors and maintainers
can inspect without reconstructing terminal output.
This also fixes the desktop smoke current-runtime path so browser-driven smoke
runs use the desktop default active provider instead of forcing the official
current model, and records that runtime decision as an artifact.
Constraint: Default PR gates must remain non-live and contributor-safe while live model checks stay explicit.
Constraint: Release packaging is still GitHub Actions based, so release preflight must run before the build matrix.
Rejected: Make live provider or desktop smoke mandatory on every PR | secrets, quotas, and model availability are maintainer-controlled.
Rejected: Let PRs lower coverage baselines in the same change | base-branch ratchet comparison must remain authoritative.
Confidence: high
Scope-risk: moderate
Directive: Do not relax coverage or quarantine policy without a maintainer approval label and a fresh quality report.
Tested: ALLOW_CLI_CORE_CHANGE=1 ALLOW_COVERAGE_BASELINE_CHANGE=1 bun run quality:gate --mode pr
Tested: bun run quality:gate --mode baseline --allow-live --only provider-smoke:* --provider-model nvidia-custom:main:nvidia-custom-main --artifacts-dir /tmp/quality-gate-live-smoke
Tested: bun run quality:gate --mode baseline --allow-live --only desktop-smoke:* --provider-model current:current:current-runtime --artifacts-dir /tmp/quality-gate-desktop-smoke-fixed
Tested: git diff --check
Not-tested: Full live release mode with multiple providers in hosted CI; provider credentials and quota remain maintainer-controlled.
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
Context inspection was switching from the local context estimate to the latest
provider usage total once a response completed. That provider total omitted the
assistant output tokens, even though those tokens become part of the next turn's
context, so the desktop meter could fall from 11% to 10% after work finished.
The context total now includes output tokens and keeps the local estimate as a
lower bound.
Constraint: Provider usage and local context estimates use different token accounting paths
Rejected: Trust provider input tokens alone | it omits the latest assistant output from the next-turn context
Confidence: high
Scope-risk: narrow
Directive: Context usage totals must represent the next-turn conversation context, not billing-only input tokens
Tested: bun test src/utils/__tests__/context.test.ts
Tested: bun run check:server
Remote main already contains PR #217. Local main had provider context-window and auth-strategy work, so this merge keeps both context resolvers and reconciles the desktop Bun lockfile with the merged desktop package manifest. The accidental remote .nvimlog artifact is excluded from the merge result.
Constraint: Local main was 15 commits ahead and 8 commits behind origin/main after PR #217 was merged remotely
Rejected: Fast-forward pull | local main had unpushed commits
Rejected: Keep remote desktop lockfile verbatim | failed bun install --frozen-lockfile against the merged package.json
Confidence: high
Scope-risk: broad
Directive: Keep provider-configured context windows ahead of OpenAI OAuth model windows so explicit user/provider settings win
Tested: bun install --frozen-lockfile; cd desktop && bun install --frozen-lockfile; git diff --check; bun run check:server; bun run check:desktop; ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Not-tested: live provider OAuth login with a real OpenAI account
PR 217 combined OpenAI OAuth model options with env-configured provider models, but the picker path returned before appending third-party model aliases. The desktop workspace test also exercised Prism highlighting for every expanded line, which was costly enough to time out on CI runners. The docs job did not need the root React Vite plugin; the desktop package owns that dependency.\n\nConstraint: PR Quality must pass server, desktop, and docs jobs before merge.\nRejected: Increase Vitest timeout | keeps the expensive render path and hides the CI regression.\nRejected: Add root Vite 8 peer dependencies | root only builds VitePress docs; React Vite plugin belongs in desktop/.\nConfidence: high\nScope-risk: narrow\nTested: npm ci --ignore-scripts; npm run docs:build; bun test src/server/__tests__/settings.test.ts src/server/__tests__/conversation-service.test.ts; bun run check:server; bun run check:desktop\nNot-tested: live provider OAuth login
Teach provider presets and saved providers to carry per-model context windows so auto-compact uses provider-specific limits instead of a blanket fallback. The desktop provider form now keeps these limits in an advanced context section and syncs the generated settings preview with model changes.
Constraint: Third-party provider APIs do not consistently expose machine-readable model context windows.
Rejected: Keep a single 100K/200K fallback | modern provider windows vary from 128K to 1M+ and the fallback caused late or early compaction.
Confidence: high
Scope-risk: moderate
Directive: Do not change provider compact thresholds without re-running real provider long-context checks.
Tested: bun test src/services/compact/autoCompact.test.ts; bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts; bun run check:server; bun run check:desktop; bun run check:policy; bun run check:native; MiniMax/Kimi/DeepSeek live long-context probes; agent-browser provider form flow.
Not-tested: quality:pr policy gate still requires explicit allow-cli-core-change for CLI core edits.
Resolve the remaining conflict in the Claude API request builder by preserving the contributor's Azure OpenAI response support while keeping main's explicit disabled-thinking handling and effort suppression.
Constraint: PR #21 was conflict-blocked against origin/main and maintainer edits are enabled on the fork branch
Rejected: Rebase the contributor branch | would rewrite a long external contribution history unnecessarily
Confidence: high
Scope-risk: moderate
Directive: Keep disabled-thinking effort suppression when changing provider request mapping
Tested: bun test tests/azureOpenAI.test.ts src/utils/__tests__/thinking.test.ts
Tested: bun run check:server
Native Anthropic web search is only reliable for Claude-family model names,
while many configured providers either do not implement the server tool schema
or reject it through proxy layers. This routes WebSearch through a single
resolver, adds Tavily and Brave fallbacks, and exposes provider key setup in
the desktop settings page.
Constraint: Third-party Anthropic-compatible endpoints may reject web_search_20250305 even when the model name is Claude-like
Constraint: Non-Claude models still need a usable WebSearch path when users configure an external search provider
Rejected: Gate native WebSearch by base URL | third-party Claude proxies can support it and official-looking URLs are not the real capability boundary
Rejected: Always expose Anthropic native WebSearch | unsupported providers loop on schema or tool errors
Confidence: high
Scope-risk: moderate
Directive: Keep WebSearch capability resolution centralized in WebSearchTool/backend.ts before adding more search providers
Tested: bun test src/tools/WebSearchTool/backend.test.ts
Tested: cd desktop && bun run test -- generalSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: agent-browser Settings E2E for Tavily/Brave links and settings persistence
Tested: live Tavily and Brave fallback searches returned results using transient keys
Not-tested: automatic LLM decision to invoke WebSearch end-to-end against a paid model session
Desktop users can now disable thinking for new sessions, and Anthropic-compatible providers that opt in receive explicit disabled-thinking requests across main turns, side queries, and AI title generation.
Constraint: DeepSeek/Kimi/GLM Anthropic-compatible endpoints need a non-thinking path without scattering provider-specific logic through the CLI.
Rejected: Per-model if/else branches | centralized provider preset env keeps the native CLI surface smaller and easier to audit.
Confidence: high
Scope-risk: moderate
Tested: bun test src/utils/__tests__/thinking.test.ts src/server/__tests__/title-service.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run test -- generalSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: agent-browser DeepSeek desktop E2E with transparent proxy captured main and title requests with thinking.type=disabled
The session inspector now asks the resumed CLI for a fast structural context estimate instead of forcing the full token-counting API path. This preserves live CLI state for system prompt, tools, MCP tools, skills, and messages while avoiding the 20s timeout that made historical sessions appear stuck or fall back to transcript-only estimates.
Constraint: Inspector requests must return quickly for third-party providers and historical resumed sessions.
Rejected: Increase the server timeout | the slow path can still block on provider token counting and keeps the UI feeling broken.
Confidence: high
Scope-risk: narrow
Directive: Keep interactive inspector context on the estimateOnly control path unless the UI explicitly supports a slow precise refresh.
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: direct /api/sessions/:id/inspection?includeContext=1 returned live context in 0.046867s with System prompt, System tools, MCP tools, Messages
Tested: agent-browser automation verified no loading/error and visible System prompt/System tools/MCP tools/Messages
Not-tested: bare root tsc --noEmit, because current tsconfig scans existing desktop/src-tauri/target generated binary assets unrelated to this change
Default desktop sessions with an active third-party provider were only passing the selected model into the CLI startup path. That allowed the child CLI to fall back to stale cc-haha settings env instead of the provider index, so the UI-selected provider could diverge from the key and endpoint used by the subprocess.
Route default sessions through the active provider id and make third-party Sonnet capability overrides explicit with a non-empty sentinel. This keeps provider runtime env sourced from providers.json and avoids treating third-party Sonnet models as first-party thinking-capable models.
Constraint: Third-party provider behavior is the product baseline for desktop users.
Rejected: Rely on synced cc-haha/settings.json env | it can be stale and caused provider/key routing mismatches.
Confidence: high
Scope-risk: narrow
Directive: Third-party desktop sessions must carry providerId into ConversationService; do not depend on global settings env for active providers.
Tested: bun test src/server/__tests__/conversations.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/provider-presets.test.ts src/utils/__tests__/thinking.test.ts
Tested: git diff --check
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
Desktop sessions can switch provider and model while a CLI subprocess is already alive, so the server now serializes runtime restarts and marks provider-managed launches to prevent stale settings env from overriding the selected provider. Provider settings also write API key env consistently and clear stale managed keys before syncing.
This includes the related desktop/docs brand asset refresh and keeps the desktop locale default in Chinese, with tests updated to match the current provider semantics.
Constraint: Session-scoped model selection must win over cc-haha/settings.json and inherited ANTHROPIC_* values.
Rejected: Store the selected model as a global provider activeModel | chat runtime selection is per session.
Confidence: high
Scope-risk: moderate
Directive: Do not remove CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST without validating Desktop provider switching against stale settings env.
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/conversations.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts
Tested: cd desktop && bun run test src/stores/settingsStore.test.ts
Tested: cd desktop && bun run lint
Tested: git diff --check
Not-tested: Full desktop production package/signing.
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 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
Desktop sessions were missing a visible request_access approval path and could
mis-detect their own app window as an unapproved frontmost target, which caused
Computer Use clicks to fail even after opening the intended app. On macOS, text
entry was also split across inconsistent clipboard and keystroke paths, making
Electron inputs unreliable for Chinese and short strings.
This change adds a desktop approval bridge over the existing session websocket,
renders a dedicated desktop approval modal, threads the real desktop bundle id
into the Computer Use executor, and switches macOS clipboard typing onto the
native pasteboard plus system paste shortcut path. It also makes tool error
results expandable in the desktop chat UI so frontmost-gate failures are fully
visible during debugging.
Constraint: Desktop sessions run the CLI over the SDK websocket path, so Ink tool JSX dialogs are not visible there
Constraint: macOS IME and Electron text inputs are unreliable with pyautogui.write and generic hotkey synthesis
Rejected: Reuse CLI setToolJSX dialogs in desktop mode | no transport for mid-call Ink UI over the SDK bridge
Rejected: Keep shell pbcopy/pbpaste for clipboard typing | inconsistent with NSPasteboard path and less reliable for Chinese text
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop Computer Use approvals and macOS text-entry behavior on a single bridge/path; avoid reintroducing separate CLI-only and desktop-only codepaths for the same action
Tested: python3 -m unittest runtime/test_helpers.py
Tested: bun test src/utils/computerUse/permissions.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test ComputerUsePermissionModal chatStore
Tested: cd desktop && bun run test chatBlocks
Tested: cd desktop && bun run lint
Not-tested: End-to-end manual Computer Use interaction against a live Electron target app on macOS
Windows desktop sessions were failing during early hook execution because the
runtime assumed Git Bash was always installed. That made a missing Git
installation look like a fatal CLI startup regression even though PowerShell is
usually available and sufficient for default hook execution.
This change turns Git Bash lookup into a best-effort probe, leaves SHELL unset
when Git Bash is missing, and falls back to PowerShell for default Windows hook
execution. Explicit bash hooks still fail with a direct installation hint, but
they no longer hard-exit the whole process.
Constraint: Windows users may have PowerShell but no Git for Windows installed
Rejected: Keep hard-exiting on missing Git Bash | makes first-run desktop chat fail for a recoverable missing dependency
Rejected: Bundle Git for Windows into the app | larger distribution and higher maintenance burden than a runtime fallback
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Treat Git Bash as an optional Windows capability unless a code path explicitly requires bash semantics
Tested: bun test src/server/__tests__/conversation-service.test.ts
Tested: bun -e "await import('./src/utils/windowsPaths.ts'); await import('./src/utils/hooks.ts'); console.log('module-import-ok')"
Not-tested: End-to-end desktop chat startup on a clean Windows Server host without Git installed
Not-tested: Explicit bash hook execution on Windows after the fallback path
Related: GitHub issue #62