Release is gated on the tag only. `bun run verify` runs on PRs and locally,
not at release time, so a failing quality gate no longer blocks publishing.
- release-desktop.yml: remove the quality-preflight job and its `needs`.
- release-workflow.test.ts: assert the release workflow does NOT run verify.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Intro now explains why we migrated: users reported macOS performance
issues and Windows behaviour diverging from the macOS dev environment;
Electron's bundled Chromium unifies the rendering layer at the cost of a
larger installer.
- Fixes section rebuilt from the real v0.3.2..HEAD commits, keeping the
upstream issue numbers (#665#653#351#672#678#671#681#658#651#620)
so they can be closed against this release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Real-world testing showed the unsigned app just needs the original 0.3.2
one-liner after the "damaged" prompt (System Settings "Open Anyway" also
works). Drop the Sequoia / "don't trash it" / clear-DMG-first / script
walkthrough noise and match the historical release-note style.
- release-notes/v0.4.0.md: macOS = drag in + `xattr -cr ...`, Windows one
line, Linux kept short.
- docs/desktop/04-installation.md: same trim, remove duplicated FAQ entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unsigned builds ship manual downloads only, so the macOS auto-update zip
(~187MB) and the blockmaps just clutter the release page and dev artifacts.
- release-desktop.yml: stop attaching *.zip / *.blockmap to the GitHub
Release; users now see DMG/exe/AppImage/deb + install-macos-unsigned.sh.
- build-desktop-dev.yml: stop collecting *.zip / *.blockmap into the dev
artifact for the same reason.
- electron-builder still produces dmg+zip and the whole updater-metadata
pipeline (latest*.yml merge/validate) is untouched, so auto-update can be
re-enabled later by restoring the two asset globs.
- sync release-workflow.test.ts dev-artifact assertion accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use frameless Electron chrome only on Windows and remove the native Windows application menu so the packaged app matches the previous Tauri-style desktop surface.
Add a Windows-only manual drag fallback for desktop drag regions, while excluding tab reorder targets and preserving macOS/Linux native chrome and menu behavior.
Tested: cd desktop && bun run test --run electron/services/menu.test.ts electron/services/windows.test.ts src/hooks/useElectronWindowDragRegions.test.tsx src/components/layout/TabBar.test.tsx src/components/layout/Sidebar.test.tsx src/lib/desktopHost/electronHost.test.ts electron/ipc/capabilities.test.ts
Tested: cd desktop && SKIP_INSTALL=1 bun run build:windows-x64
Tested: Computer Use Windows packaged app smoke verified no native menu, custom controls, drag regions, tab reorder, close-to-background, and deepseek-v4-pro provider response FINAL_WINDOWS_ELECTRON_REAL_PROVIDER_OK.
Not-tested: full bun run verify.
Constraint: keep custom frameless behavior scoped to win32 so macOS and Linux native chrome paths remain intact.
Confidence: high
Scope-risk: moderate
- Rewrite release-notes/v0.4.0.md to match the historical style (no emoji,
Highlights/Fixes/Notes), keep it user-facing only (drop release-process
notes), and correct Linux (supported since the Tauri builds, not new).
- README.md / README.en.md: replace Tauri references with Electron, list
macOS / Windows / Linux, and point first-launch approval to the guide.
- Rewrite docs/desktop/04-installation.md for Electron: Electron asset
names, Linux section, and the unsigned-macOS flow (clear the DMG quarantine
before double-clicking; drop the WebView2/right-click-open leftovers).
- install-macos-unsigned.sh: keep the same-folder DMG flow, no online download.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove maintainer-facing release workflow notes from the public release markdown and keep the macOS unsigned install guidance concise.
Tested: git diff --check
Tested: bun run check:docs
Confidence: high
Scope-risk: narrow
Bump the Electron desktop package version to 0.4.0, publish a macOS unsigned install helper, and let the release workflow continue when Developer ID signing is not configured.
Tested: bash -n desktop/scripts/install-macos-unsigned.sh
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: bun run scripts/release.ts 0.4.0 --dry
Tested: git diff --check
Tested: bun run check:docs
Tested: bun run check:policy
Confidence: high
Scope-risk: moderate
Summarize the v0.3.2 to v0.4.0 Electron migration window and document the installer paths users should follow.
Tested: bun run scripts/release.ts 0.4.0 --dry
Tested: git diff --check
Tested: bun run check:docs
Confidence: high
Scope-risk: narrow
Keep development desktop artifacts limited to distributable archives and update
metadata so macOS workflow downloads do not duplicate the packaged app bundle.
The package smoke step still validates the unpacked app before upload.
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: bun run check:policy
Confidence: high
Scope-risk: narrow
Match Electron Builder Linux output by accepting linux-*-unpacked directories,
treating AppImage blockmaps as optional, and validating release asset names
against the generated x86_64/amd64 and arm64 artifacts.
Tested: bun test scripts/quality-gate/package-smoke/index.test.ts
Tested: bun test scripts/pr/release-workflow.test.ts scripts/quality-gate/package-smoke/index.test.ts
Tested: bun run check:policy
Tested: bun run check:native
Tested: bun run verify
Confidence: high
Scope-risk: moderate
Ensure Electron Builder has the project URL and maintainer metadata required by Linux deb targets, and lock the fields with release workflow coverage.
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: bun run check:policy
Tested: bun run check:native
Tested: bun run verify
Confidence: high
Scope-risk: narrow
Replace the local pre-push quality gate with a reminder-only hook, keep manual quality commands documented, and update the contract test to prevent reintroducing a blocking push gate.
Tested: bash .git/hooks/pre-push </dev/null
Tested: bun test scripts/git-hooks/install.test.ts scripts/pr/quality-contract.test.ts
Tested: bun run check:policy
Tested: bun run check:docs
Tested: git diff --check
Confidence: high
Scope-risk: narrow
Use OS-assigned ports for server integration tests and publish the actual Bun server port after startup, preventing local port collisions from failing server checks.
Tested: bun test src/server/__tests__/e2e/business-flow.test.ts
Tested: bun test src/server/__tests__/e2e/full-flow.test.ts src/server/__tests__/conversations.test.ts src/server/__tests__/tasks.test.ts src/server/__tests__/haha-openai-oauth-api.test.ts
Tested: bun run check:server
Confidence: high
Scope-risk: narrow
Use container queries for the activity summary grid so the Token usage panel responds to its own width instead of the full desktop viewport. This prevents loose medium-width layouts and over-compressed five-column cards when the settings page is shown with sidebars.
Tested:
- cd desktop && bun run test -- src/pages/ActivitySettings.test.tsx --run --reporter=verbose --pool=forks --maxWorkers=1 --minWorkers=1
- cd desktop && bun run test -- src/theme/globals.test.ts --run --reporter=verbose --pool=forks --maxWorkers=1 --minWorkers=1
- bun run check:desktop
- Electron smoke at 1180px and 900px against the local dev server
Not-tested:
- Coverage report; this is a presentation-only desktop UI change.
Confidence: high
Scope-risk: narrow
The sidebar title area also needs to participate in Electron's CSS app-region chrome. Mark only the sidebar title/header region as draggable and keep the full sidebar body out of the drag region so session rows, search controls, and project actions remain normal interactions.
Constraint: Electron window dragging is driven by CSS app-region regions after the migration
Rejected: Keep the sidebar-wide startDragging mouse handler | Electron does not use that as the active drag path and it misses the left title gap
Confidence: high
Scope-risk: narrow
Directive: Keep sidebar body interactions outside the drag region unless a real titlebar affordance is added there
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx --run
Tested: bun run check:desktop
Separate quarantine review enforcement from server and coverage file selection so expired review dates fail only the governance lane.
Refresh stale server quarantine suites and keep only the live provider test quarantined for non-live PR gates.
Tested: bun run check:policy
Tested: bun run check:server
Tested: bun run check:coverage
Tested: bun run verify
Tested: git diff --check
Confidence: high
Scope-risk: moderate
Drop the explanatory comment added earlier - notifications work as-is, so the
notification service stays byte-for-byte identical to main.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a CSP meta tag that hardens default-src/object-src/base-uri while keeping
script/style 'unsafe-inline'+'unsafe-eval' (Emotion CSS-in-JS injects runtime
<style> tags, the startup watchdog is inline, shiki/mermaid/Vite use eval) and a
permissive connect-src (localhost sidecar + ws + https) so renderer fetches and
dev HMR keep working. Needs a runtime smoke on dev + packaged builds before any
tightening.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- serverRuntime injects CLAUDE_CODE_POWERSHELL_PATH from the user's chosen shell
(readDesktopTerminalConfig + resolveDesktopTerminalShell) on Windows, so the
agent PowerShellTool honors the same shell as the UI terminal. Best-effort:
never blocks startup, never overrides an explicit env var, only forwards
pwsh/powershell selections (not cmd/custom). Regression from the Tauri build.
- document that notificationPermissionState reflects OS capability, not macOS
authorization (no Electron API exists; the 'failed' lifecycle is the real
signal) instead of calling the non-existent systemPreferences.getNotificationSettings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
killSidecar gains a sync flag (spawnSync taskkill on Windows); serverRuntime
stopAll threads it through; before-quit now shuts down synchronously so the
Windows taskkill completes before the process exits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- applyWindowsAppUserModelId() so Windows attributes toast notifications to the
app (kept in sync with build.appId via a test); no-op on macOS/Linux
- main window: setWindowOpenHandler denies uncontrolled popups, routes http(s)
links to the system browser; intentionally no will-navigate guard so dev HMR
reloads keep working
- preview view: denies popups + blocks non-http(s) navigation (file:/custom
schemes) while allowing in-page http(s) browsing
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- mac.notarize=true + hardenedRuntime + entitlements so a signed CI release
actually notarizes (gatekeeper smoke + Squirrel.Mac auto-update need it)
- entitlements grant disable-library-validation for the Bun sidecar/node-pty
- local unsigned build passes -c.mac.notarize=false so electron:package still
works without an Apple account
- release signing-preflight now hard-requires only the Apple secrets; Windows
cert is optional (unsigned NSIS still auto-updates, just SmartScreen warning)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bare absolute paths like /icons/bilibili.svg failed under Electron's
file:// protocol. Route them through publicAssetPath so they resolve
against the relative base URL, matching the GitHub and app icons.
The Electron packaging output directory is generated locally and should stay out of source control alongside the existing desktop build output ignores.
Constraint: desktop/electron-dist/ is local build output, not a product source artifact.
Confidence: high
Scope-risk: narrow
Tested: git check-ignore -v desktop/electron-dist/
Tested: git diff --check
Electron handles custom draggable chrome through CSS app-region rules, not the old runtime startDragging path. Mark the tab strip and empty scroll gutter as native drag regions while keeping tab items and controls explicitly no-drag so tab reordering and close/tool buttons keep receiving pointer events.
Constraint: Electron drag regions swallow pointer events unless interactive children are marked no-drag
Rejected: Keep calling startDragging from the empty gutter | Electron desktopHost does not expose that as the active migration path
Confidence: high
Scope-risk: narrow
Directive: Do not mark tab items themselves as drag regions without revalidating tab reorder behavior
Tested: cd desktop && bun run test src/components/layout/TabBar.test.tsx --run
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
/agent should behave like a normal session prompt that tells the main model to use the selected Agent tool. The previous forked command path buffered the subagent run behind local command output and forced desktop clients to wait for command completion instead of seeing the normal Agent interaction stream.
Constraint: Desktop /agent must preserve ordinary chat/session semantics and still require /agent <agent> <prompt>.
Rejected: Keep slash_agent SDK event bridging | it preserves the separate forked execution path instead of fixing the product flow.
Confidence: high
Scope-risk: moderate
Directive: Do not move /agent back to context: fork without proving the desktop normal Agent tool stream still works.
Tested: bun test src/commands/agent.test.ts src/utils/processUserInput/processSlashCommand.test.ts src/server/__tests__/ws-memory-events.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
Tested: git diff --check
Not-tested: bun run check:server is blocked before source checks by expired quarantine entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
The chat reference action was still tied too closely to message-local
mouseup timing, and the floating fixed-position control stayed inside
virtualized message DOM. In Electron Chromium that made multi-line ranges
race selection settlement and let transformed ancestors offset the button
away from the viewport position we calculated.
This reads settled document selections after pointerup/selectionchange,
portals the action to document.body, and prefers right-side placement for
multi-line selections.
Constraint: Electron Chromium selectionchange can settle after message-local mouse events
Rejected: Keep the popover inside the message node | transformed/virtualized ancestors offset fixed positioning in real browser layout
Confidence: high
Scope-risk: narrow
Directive: Keep chat selection actions portaled; do not reparent under virtualized message rows without browser-coordinate verification
Tested: cd desktop && bun run test -- src/components/chat/MessageList.test.tsx
Tested: bun run check:desktop
Tested: Playwright Chromium smoke for multi-line selection, single-line selection, and outside-click dismissal on local desktop harness
The desktop /agent slash path runs before the normal query loop, so the old implementation waited for the forked command to finish and only surfaced the final local-command stdout. Emit foreground slash-agent task events from /agent, drain SDK events while QueryEngine waits for pre-query slash processing, and allow those current-turn events through the WebSocket pre-send mute gate.
Constraint: /agent is a forked slash command that does not produce ordinary assistant deltas until its final stdout is synthesized.
Rejected: Frontend-only loading state | would not expose real forked-agent progress or tool/tool-result updates.
Confidence: high
Scope-risk: moderate
Directive: Keep slash_agent task events scoped to /agent; ordinary slash command stdout and /goal event handling should remain on the existing local-command paths.
Tested: bun test src/QueryEngine.test.ts src/utils/processUserInput/processSlashCommand.test.ts src/server/__tests__/ws-memory-events.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
Not-tested: bun run check:server is blocked by expired quarantine review entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Async title refresh runs after assistant output, so provider prompts can be biased by assistant language and English title examples. The title language now resolves from the first meaningful user message before consulting the response-language fallback, then retries once when a generated title violates that language.
Constraint: Desktop response-language settings still control assistant prose, not automatic title metadata when the first user message has a clear language
Rejected: Follow response-language setting for every generated title | Chinese sessions flip to English when replies are configured in English
Rejected: Persist mismatched generated titles after retry failure | preserves a bad UX over the safer first-message placeholder
Confidence: high
Scope-risk: moderate
Directive: Do not change automatic title language precedence without re-testing image/text first-turn sessions and English-response-language sessions
Tested: bun test src/server/__tests__/title-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "refreshes the first-turn AI title"
Tested: git diff --check
Tested: real MiniMax-M3 /tmp session 58a47fcd-6f9d-4685-bc73-f9317e3f330e with temporary response language english; assistant replied English and final aiTitle was 标题语言验证
Not-tested: bun run check:server blocked before tests by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Desktop title generation previously upgraded the first sidebar title from the raw user prompt before assistant output existed. That made image-plus-text sessions vulnerable to keeping a title based only on the user's initial wording. This moves polished title generation to completed turns, reuses the CLI title prompt and transcript extractor, and routes title calls through the configured haiku model with a retry when disabled thinking is rejected.
Constraint: The first user message still needs an immediate placeholder title so the sidebar is responsive.
Rejected: Generate the AI title before assistant output | image-derived context is only available after the assistant turn completes.
Confidence: high
Scope-risk: moderate
Directive: Keep polished automatic titles tied to completed transcript turns, not pre-response user prompts.
Tested: bun test src/server/__tests__/title-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "refreshes the first-turn AI title"
Tested: git diff --check
Tested: Real MiniMax-M3 /tmp session f89073ed-3467-492a-b63c-e1886c146a3a with image attachment wrote ai-title "Validate session title generation"
Not-tested: bun run check:server is blocked by existing expired quarantine manifest entries
MiniMax's official M3 model page and release notes describe M3 as supporting up to a 1M-token context window with a guaranteed minimum of 512K. Keep the built-in MiniMax preset aligned with that official default instead of inheriting the older 204.8K MiniMax generation limit.
Constraint: Official MiniMax M3 docs state the M3 API supports up to 1M tokens context window.
Rejected: Keep 204800 and rely on manual provider overrides | that makes the built-in MiniMax preset wrong for new users.
Confidence: high
Scope-risk: narrow
Directive: Do not copy MiniMax-M2.7's 204.8K window onto MiniMax-M3 without re-checking official M3 docs.
Related: https://www.minimax.io/models/text/m3
Related: https://www.minimax.io/blog/minimax-m3
Tested: git diff --check
Tested: bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers-real.test.ts src/server/__tests__/provider-runtime-env.test.ts src/server/__tests__/providers.test.ts src/utils/__tests__/context.test.ts
Not-tested: bun run check:server blocked before execution by expired quarantine entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Desktop chat can fall back to persisted transcript estimates when live inspection context is unavailable. That path previously only used built-in model windows, so custom provider settings such as MiniMax-M3 with a 1,000,000-token window still displayed the default 200,000-token estimate. Resolve transcript context windows from the session or active provider runtime env first, then fall back to built-ins.
Constraint: Provider model context windows are stored in provider runtime env, not always in the server process env.
Rejected: Change the built-in MiniMax-M3 window to 1,000,000 | upstream preset correctly keeps MiniMax-M3 at 204,800 by default, while user overrides must remain provider-specific.
Confidence: high
Scope-risk: moderate
Directive: Keep transcript estimates aligned with session/provider runtime env before consulting built-in model defaults.
Tested: git diff --check
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "active provider model context windows|Sonnet 4.6 transcript usage|low-trust media"
Tested: bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/provider-runtime-env.test.ts src/server/__tests__/providers.test.ts src/utils/__tests__/context.test.ts src/utils/__tests__/contextBudget.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/provider-runtime-env.test.ts src/utils/__tests__/context.test.ts src/utils/__tests__/contextBudget.test.ts
Not-tested: bun run check:server blocked before execution by expired quarantine entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Remote main includes PR #694, which updates the MiniMax preset to MiniMax-M3 and removes deprecated built-in MiniMax context-window entries. Merge that provider baseline before applying the transcript context-window estimate fix so the local line keeps upstream provider defaults.
Constraint: Remote main advanced independently with PR #694 while local main carried unreleased commits.
Rejected: Reset local main to origin/main | would discard local-only main commits.
Confidence: high
Scope-risk: moderate
Directive: Preserve MiniMax-M3 provider preset changes when touching provider context-window logic.
Tested: bun test src/server/__tests__/conversations.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/provider-runtime-env.test.ts src/utils/__tests__/context.test.ts src/utils/__tests__/contextBudget.test.ts
Not-tested: bun run check:server blocked by expired quarantine entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Chat selection references could miss drag gestures that ended outside the
message bubble after the Electron migration. The chat transcript now tracks
selection gestures at the document pointer-up boundary and repositions the
shared selection action near the selected text with above-then-right placement.
Constraint: Electron/WebView selection ranges are not always stable during the message-local mouseup event
Rejected: Add a new selection popover system | the existing shared selection hook already covers chat and workspace dismissal behavior
Confidence: high
Scope-risk: narrow
Directive: Keep chat and workspace selection popovers on the shared positioning/dismissal hook unless their behavior intentionally diverges
Tested: bun run test -- src/components/chat/MessageList.test.tsx -t "selected-message action"
Tested: bun run test -- src/components/chat/MessageList.test.tsx
Tested: bun run check:desktop
Tested: in-app browser smoke at http://127.0.0.1:5181/ with zero console errors
Not-tested: Packaged Electron manual drag selection smoke
Related: #351
Selecting a concrete /agent entry from the composer suggestions should prepare the command and leave focus in the editor so the user can type the required prompt. The Enter send shortcut still applies after the suggestion menu is closed, and exact non-agent slash commands keep their existing direct execution path when they remain highlighted.
Constraint: The desktop composer supports configurable Enter vs Ctrl/Cmd+Enter sending.
Rejected: Special-case only /agent entries | the highlighted suggestion should consistently win while the menu is open.
Confidence: high
Scope-risk: narrow
Directive: Keep ChatInput and EmptySession slash-key handling aligned with shared send shortcut behavior.
Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: bun run check:desktop
Not-tested: Live backend agent-list integration beyond mocked desktop composer coverage.
Electron was showing the main BrowserWindow before renderer load completed, so macOS could paint a blank window surface behind the menu bar. Persisted y=0 window state could also restore the titlebar under the menu-bar work area on external displays. Delay the initial show until the renderer entry is loaded, clamp restored macOS bounds into the display workArea, skip the macOS status-bar tray, and unhide the app before focusing restored windows.
Constraint: macOS external displays expose menu-bar and status-item chrome differently from Windows and Linux.
Rejected: Remove the native application menu | it would break expected macOS menu behavior without fixing the pre-load window surface.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce pre-load main-window show without validating macOS external-display menu-bar rendering.
Tested: cd desktop && bun test electron/services/windows.test.ts electron/services/tray.test.ts electron/services/singleInstance.test.ts electron/services/menu.test.ts
Tested: bun run check:desktop
Tested: bun run check:native
Tested: git diff --check
Not-tested: Live dual-external-monitor screenshot smoke.
Complete the Electron replacement boundary before merging by removing the renderer-side Tauri host fallback, tightening H5/browser access so only desktop navigation is tokenless, and moving desktop release publication to a tag-driven GitHub Actions matrix with a single final publish job.
Constraint: H5/browser capability access must not gain tokenless access through localhost or retired Tauri origins
Constraint: Desktop release artifacts must be built by GitHub Actions from version tags, not treated as local build outputs
Rejected: Keep localhost browser origins trusted for convenience | local browser contexts can access loopback services and must use the H5 token path
Rejected: Publish from each matrix job | partial releases can be created before all platforms finish
Confidence: high
Scope-risk: broad
Directive: Do not reintroduce Tauri origins or localhost browser origins into the trusted desktop origin set without a reviewed security design
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/diagnostics-service.test.ts src/server/middleware/cors.test.ts
Tested: bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts
Tested: bun run check:desktop
Tested: bun run check:native
Tested: git diff --check
Not-tested: bun run check:server is blocked by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Electron desktop runs network-sensitive OpenAI OAuth token exchange in the sidecar process, so the sidecar now receives system proxy env derived from Electron's cross-platform proxy resolver and the OAuth token client uses the existing proxy fetch options. General manual proxy settings also document and preserve authenticated proxy URLs.
The macOS fullscreen black-screen path is addressed by avoiding native fullscreen Spaces for app fullscreen toggles and by leaving fullscreen before hiding or closing the window.
Constraint: Electron packaged apps may not inherit shell HTTPS_PROXY env when launched from Finder.
Constraint: Manual authenticated proxies must remain standard HTTP(S) proxy URLs for Bun/undici compatibility.
Rejected: Store proxy username and password as separate fields | would require new secret-storage semantics and migration beyond this bugfix.
Rejected: Use native macOS fullscreen Spaces for the desktop app | reproduced black-screen behavior when hiding/closing from fullscreen.
Confidence: high
Scope-risk: moderate
Directive: Do not remove sidecar proxy env injection without retesting OpenAI OAuth from a packaged app launched outside a shell.
Tested: bun test src/services/openaiAuth/client.test.ts src/server/__tests__/haha-openai-oauth-service.test.ts
Tested: bun test src/server/__tests__/network-settings.test.ts
Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx --run
Tested: cd desktop && bun run check:electron
Tested: git diff --check
Not-tested: bun run check:server blocked by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Not-tested: live OpenAI OAuth through a corporate authenticated proxy
Electron's sidecar runs outside app.asar, so H5 static files must be available as normal unpacked files. Point the sidecar at the unpacked renderer dist and keep a server fallback for stale app.asar-style paths.
Constraint: Packaged Bun sidecars cannot read app.asar paths with ordinary fs stat calls.
Rejected: Serve H5 from app.asar directly | the external sidecar is not Electron and does not get asar filesystem support.
Confidence: high
Scope-risk: narrow
Directive: Keep package-smoke checking app.asar.unpacked/dist/index.html before changing asarUnpack or H5 dist paths.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-policy.test.ts
Tested: bun test desktop/electron/services/sidecarManager.test.ts scripts/quality-gate/package-smoke/index.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run check:electron
Tested: git diff --check
Tested: SKIP_INSTALL=1 SIGN_BUILD=0 MAC_TARGETS=dmg desktop/scripts/build-macos-arm64.sh
Tested: packaged sidecar curl /?serverUrl=...&h5Token=... returned HTTP 200
Not-tested: Gatekeeper notarization for the local ad-hoc DMG
Add MiniMax-M3 as the new default for the minimax provider preset and
remove deprecated M2.5/M2.1/M2 entries. MiniMax-M2.7 and the highspeed
variant are kept for users who pinned the previous generation.
Updated places:
- providerPresets.json: default models -> M3, modelContextWindows trimmed
- modelContextWindows.ts: built-in window list mirrors the new set
- provider-presets / providers-real / thinking tests updated to assert
the new default
- .env.example and third-party docs (zh + en) recommend M3
- scripts/repro-issue-247-real.ts default model bumped to M3
Co-Authored-By: Octopus <liyuan851277048@icloud.com>
Introduce the Electron desktop shell alongside the existing React renderer and local Bun server boundary. The migration keeps the DesktopHost contract explicit across Tauri, Electron, and browser runtimes while adding Electron main/preload services for dialogs, shell, notifications, updates, tray/window lifecycle, terminal, preview WebContentsView, app mode, and release/package validation.
The commit also carries the latest local main desktop command updates, including agent slash entries and hidden-by-default markdown thinking details, so the packaged Electron build matches the current main UX surface.
Constraint: React renderer, local Bun server, REST/WebSocket, and sidecar boundaries must remain reusable during the migration
Constraint: macOS dev packages are ad-hoc signed and cannot prove Developer ID notarization or Gatekeeper release launch
Rejected: Browser-only smoke validation | it cannot exercise native dialogs, keychain prompts, notification behavior, or packaged app startup
Confidence: medium
Scope-risk: broad
Directive: Do not remove Tauri host support until signed Electron release artifacts pass native OS smoke on macOS, Windows, and Linux
Tested: bun run check:desktop
Tested: cd desktop && bun run check:electron
Tested: CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron📦dir
Tested: bun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron
Tested: Computer Use read packaged Electron app window at desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app
Not-tested: Developer ID signed/notarized Gatekeeper launch
Not-tested: Real OS notification click-to-session action
Not-tested: Windows and Linux packaged app smoke on real hosts
Thinking markdown should remain available only after the user opens the disclosure. The prior preview mounted markdown content while collapsed, which made internal thinking occupy visible transcript space by default.
Constraint: Thinking content must stay behind the existing disclosure while preserving markdown rendering after expansion
Rejected: Ten-line collapsed markdown preview | still exposes thinking content in the normal chat flow
Confidence: high
Scope-risk: narrow
Directive: Do not render ThinkingBlock content while aria-expanded=false; keep MarkdownRenderer inside the expanded branch
Tested: cd desktop && bun run test -- chatBlocks.test.tsx
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: Live desktop visual smoke with a real provider transcript
Related: #660
Desktop users need a direct way to pick an installed Agent from slash completion while preserving the existing command and skill namespace. Add /agent <agent> <prompt> as the single execution surface and render active agents as namespaced completion rows.
Constraint: Existing slash command names and ordering must remain authoritative.
Rejected: Register each Agent as a top-level /<agent> command | would collide with built-in commands, skills, and future custom commands.
Confidence: high
Scope-risk: moderate
Tested: bun run verify
Large screenshots were being treated like oversized text payloads,
which pushed readable images through an aggressive resize/compression
path before the model saw them. The image path now preserves images
that already fit API byte and dimension limits, prefers lossless or
high-quality compression when only bytes are too large, and only
downscales by vision pixel budget when the image would exceed the
Read token budget.
Constraint: Fixes pasted/read image failures reported in #615 and #677 while preserving screenshot readability requested in #663
Rejected: Always resize to 2048px | would unnecessarily degrade screenshots that already fit provider limits
Rejected: Continue estimating image budget from base64 text length | image blocks are billed/read as vision pixels, not text payload
Confidence: high
Scope-risk: moderate
Directive: Do not lower fallback JPEG quality or long-edge limits without revalidating real screenshot OCR/vision tasks
Tested: bun test src/utils/__tests__/imageResizer.test.ts src/server/__tests__/conversation-attachments.test.ts
Tested: bun run check:server
Tested: bun run check:coverage (changed-line coverage 95.25%; adapters/desktop coverage lanes failed from existing non-target issues)
Tested: GPT-5.5 Read smoke on 1394x4404, 4096x2304, and 7900x900 images
Not-tested: Real resize/re-encode branch in this source worktree because optional sharp/native image processor is not installed locally
Related: #615
Related: #677
Related: #663
Desktop terminal settings previously only controlled the UI terminal, so Windows sessions could show pwsh while agent PowerShell detection still fell back to Windows PowerShell 5.1. The sidecar now receives a narrow PowerShell-only override and the runtime resolves pwsh through PSHOME and standard Windows paths before falling back.
Fixes#665.
Constraint: Windows terminal shell selection must not make cmd, Git Bash, or arbitrary custom shells drive PowerShellTool execution
Rejected: Reuse the full terminal shell path for agent tools | would couple UI terminal behavior to tool execution and could route non-PowerShell shells into PowerShell detection
Confidence: medium
Scope-risk: moderate
Directive: Keep agent command shell selection narrower than terminal UI shell selection unless the permission/parser model is updated with it
Tested: bun test src/utils/shell/powershellDetection.test.ts; cd desktop/src-tauri && cargo test agent_powershell_override --lib; bun run check:native; bun run check:server; git diff --check
Not-tested: Live Windows desktop smoke with installed pwsh
Related: #665
(cherry picked from commit ecd813b9d2aebfb8d8b6d196170177eec969a6de)
Provider connectivity checks and proxy validation are API boundaries, but they kept the user-facing context suffix in the model id while the chat runtime already strips it. Normalize these paths so MiMo-style models with [1m] test the same model that sessions send.
Constraint: [1m]/[2m] are client-side context markers and upstream APIs may reject them
Rejected: Strip suffixes when saving provider config | would change the user-visible model selection instead of only the API boundary
Confidence: high
Scope-risk: narrow
Directive: Keep all provider API boundaries aligned with normalizeModelStringForAPI before sending requests upstream
Tested: bun test src/server/__tests__/providers.test.ts --test-name-pattern "normalizes context-window suffixes"
Tested: bun test src/server/__tests__/providers.test.ts
Tested: bun run check:server
Tested: git diff --check
Not-tested: Live MiMo API call; no provider credential was used
Related: #620
Support #660 by reusing the existing desktop markdown renderer for thinking content while keeping collapsed previews bounded to the first ten source lines and a ten-line visual cap.
Constraint: Issue #660 requests markdown-rendered thinking summaries with a ten-line collapsed preview and click-to-expand full content
Rejected: Keep the old single-line plaintext preview | it loses markdown structure and hides useful context
Confidence: high
Scope-risk: narrow
Directive: Keep ThinkingBlock on MarkdownRenderer instead of duplicating markdown parsing behavior
Tested: cd desktop && bun run test -- chatBlocks.test.tsx
Tested: bun run check:desktop
Not-tested: Live desktop visual smoke with a real provider transcript
Related: #660