The previous batch selection state let adjacent selected sessions visually merge into a single block. This keeps the existing row structure but adds per-row spacing, a transparent baseline border, and a subtle selected-row shadow so the selection is easier to read without changing behavior.
Constraint: Keep the change local to the desktop sidebar and avoid new dependencies.
Rejected: Add global sidebar CSS tokens | unnecessary for this small component-level state and harder for changed-line coverage.
Confidence: high
Scope-risk: narrow
Directive: Keep batch selection spacing on the row wrapper so adjacent selected rows remain visually separated.
Tested: bun run verify
The pure white appearance option needed to avoid warm-theme leakage while preserving the existing warm classic brand theme. This adds the white theme mode, keeps local browser startup from reusing stale H5 server URLs in dev, and moves visible legacy warm surfaces onto theme tokens.
Constraint: H5 server auth policy, CORS policy, SDK routes, adapter routes, and IM access paths must not change for a visual theme fix
Rejected: Rename the original light theme to pure white | the original theme is a warm classic palette, not a neutral white workspace
Confidence: high
Scope-risk: moderate
Directive: Keep structural white-theme borders neutral; reserve the warm brand color for selected states, primary actions, and small accents
Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx src/lib/desktopRuntime.test.ts src/lib/persistenceMigrations.test.ts src/stores/uiStore.test.ts src/stores/settingsStore.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: bun test src/server/__tests__/settings.test.ts src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: Browser smoke at http://127.0.0.1:5173 with data-theme=white, no H5 token prompt, /status inspector visible, inspector border #DDE3EA
Not-tested: Full bun run verify gate
The desktop chat composer is a single mounted component under the active session view, so switching tabs reused the same local draft state. The fix keeps unsent composer input and attachments keyed by session tab and restores the matching draft when the active tab changes.
Constraint: Must preserve unsent drafts while isolating them per session tab
Rejected: Force-remount ChatInput with a React key | wider UI lifecycle change and more risk to composer substate
Confidence: high
Scope-risk: narrow
Directive: Do not make composer draft state global without preserving per-session isolation
Tested: cd desktop && bun run test -- ChatInput.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: agent-browser reproduced the leak on an unmodified baseline and verified the fixed UI keeps history tab textarea empty while restoring the new tab draft
Not-tested: Windows 11 packaged release manual smoke
H5 access is a user-facing desktop sharing entrypoint, so it should not be buried inside the broad General settings surface. Move it into its own Settings tab and make the empty QR state point users at the token generation action before a scan link exists.
Constraint: Preserve the existing H5 store API and token lifecycle while changing only the desktop settings surface.
Rejected: Keep H5 access inside General with a larger visual block | still hides a strong product surface in a catch-all page.
Confidence: high
Scope-risk: narrow
Directive: Keep H5 access as a first-class settings entry unless the navigation model is redesigned holistically.
Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx src/i18n/index.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: bun run verify
Not-tested: Live H5 token generation against an enabled local H5 service; current local desktop state had H5 disabled.
Non-streaming adapter only read `output_tokens`/`input_tokens` (Anthropic-style),
but many OpenAI-compatible providers (e.g. Jiutian MoMA) return standard
`completion_tokens`/`prompt_tokens` fields. This caused a null reference
error when reading `output_tokens` from such responses.
The streaming adapter already handles `completion_tokens` correctly, and
the Azure adapter uses the same fallback pattern applied here.
Fixes#383
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The age-based quick cleanup buttons overlapped poorly with the sidebar time groups and created ambiguity around what would be deleted. Keeping batch mode focused on all/selected sessions makes the destructive path easier to understand while preserving the existing confirmation and server deletion behavior.
Constraint: Desktop batch deletion should stay routed through the existing selected-session confirmation flow.
Rejected: Rename the age-based cleanup buttons | still leaves a low-value destructive shortcut in the primary batch panel.
Confidence: high
Scope-risk: narrow
Directive: Add future destructive cleanup shortcuts only when their target set is unambiguous in the current filtered session list.
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: Manual desktop click-through after removing the buttons.
The H5 browser shell can load while deeper Settings tabs still exercise separate API routes. This adds MCP, plugin, and Agents endpoints to the H5 policy and integration matrices so the QR-token path is protected against page-level regressions.\n\nConstraint: H5 must expose existing desktop settings surfaces only after local opt-in and a valid H5 token.\nRejected: Rely on manual browser clicks alone | they missed the stopped-port failure mode and would not guard future regressions.\nConfidence: high\nScope-risk: narrow\nDirective: Add new Settings API surfaces to this H5 matrix when exposing them in the browser UI.\nTested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts\nTested: bun run check:server
The desktop sidecar can bind on LAN addresses for phone access, so the H5 settings switch must be an authorization boundary for remote capability routes, not only a token-mode toggle. Remote browser API, proxy, websocket, and SDK routes now fail closed while H5 is disabled; local desktop, Tauri, WebUI, adapter, and internal SDK paths remain tokenless. When H5 is enabled, remote API, proxy, and websocket requests must use the H5 token carried by the QR link, and the server API key cannot substitute for that H5 token.
Constraint: Desktop sidecar binds 0.0.0.0 while reporting loopback to local UI.
Constraint: Client-controlled Host and Origin headers cannot prove a local request; the boundary uses Bun requestIP instead.
Constraint: Static H5 shell and /health must still load so browser bootstrap can show a recovery flow.
Rejected: Trust loopback Host headers | LAN clients can spoof Host and Origin.
Rejected: Use ANTHROPIC_API_KEY as a remote H5 credential | it is not the phone pairing token and would weaken the QR-token boundary.
Confidence: high
Scope-risk: moderate
Directive: Do not make h5Enabled=false an open remote state for /api, /proxy, /ws, or /sdk routes.
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: bun run check:server
Browser startup previously accepted any 200 response from /health. In Vite-only runs that can be the SPA fallback HTML, which lets startup continue until a later API call fails with an opaque JSON parse error.
Constraint: The H5 shell and Vite dev server can both return 200 for unknown routes.
Rejected: Only improve the visible StartupError copy | it would still misclassify the backend readiness state
Confidence: high
Scope-risk: narrow
Directive: Keep the healthcheck shape validation aligned with src/server/index.ts /health responses
Tested: cd desktop && bun run test src/lib/desktopRuntime.test.ts
Tested: cd desktop && bun run test src/lib/desktopRuntime.test.ts src/__tests__/generalSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
H5 LAN access now uses an explicit desktop confirmation, then shows a QR launch URL that carries both the serverUrl and one-time-visible H5 token. Browser startup consumes that token from the QR URL, verifies it before storing, and requires auth for non-loopback H5 endpoints while preserving local desktop bootstrap behavior.
Constraint: LAN H5 exposes desktop capabilities and must be opt-in with a visible warning.
Constraint: QR launch has to preserve routed public URLs, so the link carries serverUrl as well as h5Token.
Rejected: Store the raw token in Zustand | it would make accidental UI diagnostics or snapshots more likely to leak it
Confidence: high
Scope-risk: moderate
Directive: Do not remove the serverUrl query parameter from QR links without testing non-root publicBaseUrl deployments
Tested: cd desktop && bun run test src/lib/desktopRuntime.test.ts src/__tests__/generalSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Remote browser access now needs the H5 token once LAN mode is enabled, but changing that mode remains a local desktop control-plane operation even when deployment auth is explicitly enabled. CORS also no longer treats a non-local same-origin alias as trusted unless it is the configured H5 public origin or allowlisted origin.
Constraint: H5 exposes desktop control surfaces on the LAN and must not let remote browsers enable their own access.
Constraint: Desktop WebView, localhost WebUI, SDK websocket, and IM adapter loopback calls must remain tokenless.
Rejected: Allow authenticated remote control-plane writes under SERVER_AUTH_REQUIRED | violates the local opt-in boundary for H5 exposure.
Confidence: high
Scope-risk: moderate
Directive: Do not relax /api/h5-access mutating endpoints for browser origins without adding a reviewed desktop-only trust signal.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/middleware/cors.test.ts
Tested: bun run check:server
Wire request-scoped H5 auth and CORS decisions through the server so LAN browser API, proxy, and websocket access require the H5 token while loopback desktop, Tauri WebView, local adapter, SDK, and static shell paths keep their existing bootstrap behavior.
Constraint: Task 2 scope is limited to server auth/CORS wiring and the four requested files
Rejected: Global auth toggle for all requests | would incorrectly block loopback desktop and bootstrap asset flows
Directive: Keep static H5 shell and assets tokenless unless the browser-side bootstrap model changes
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/middleware/cors.test.ts
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts | h5-access-auth blocked by missing zod dependency in src/server/api/haha-oauth.ts
Tested: LSP diagnostics clean for src/server/index.ts, src/server/middleware/cors.ts, src/server/__tests__/h5-access-auth.test.ts, src/server/middleware/cors.test.ts
Not-tested: h5-access-auth runtime assertions because this checkout is missing zod for haha-oauth imports
The classifier should not treat every /sdk path as trusted by pathname, because LAN-exposed servers already have a narrower local SDK trust boundary.
Constraint: Desktop local chat, Tauri WebView, loopback adapters, and local SDK routes must remain tokenless.
Rejected: Path-only /sdk trust | a non-local caller could reach the SDK route through a LAN-bound server.
Confidence: high
Scope-risk: narrow
Directive: Keep remote /sdk and hostile-origin loopback requests in the H5/browser bucket unless a dedicated SDK auth layer is verified.
Tested: bun test src/server/__tests__/h5-access-policy.test.ts
Add a small standalone request classifier and focused server tests so later H5 token enforcement can target LAN/browser requests without changing the current desktop, SDK, or adapter paths.
Constraint: Task scope is limited to a standalone classifier plus tests in two server files
Constraint: Local desktop WebView, loopback integrations, and /sdk routes must remain tokenless for now
Rejected: Integrate the classifier into auth middleware now | this task only introduces the reusable classification seam
Confidence: high
Scope-risk: narrow
Directive: Keep local-trusted and internal-sdk classifications tokenless unless the server auth integration changes with matching regression coverage
Tested: bun test src/server/__tests__/h5-access-policy.test.ts
Tested: per-file TypeScript diagnostics for src/server/h5AccessPolicy.ts and src/server/__tests__/h5-access-policy.test.ts
Not-tested: Full server auth middleware integration
Not-tested: bun run check:server remains red from pre-existing repo dependency and unrelated test failures
The worktree implementation is ready for local mainline testing, and main
only advanced with unrelated chat URL wrapping. Merge the completed batch
session-management change so the desktop UI and server API move together.
Constraint: Main already contains an unrelated chat URL wrapping fix
Confidence: high
Scope-risk: moderate
Directive: Keep the merged feature commit intact; it carries the detailed implementation rationale and verification record
Tested: Feature commit verified with server batch tests, server gate, desktop gate, and browser UI smoke before merge
Not-tested: Full verify rerun after merge; merge touched no conflicting files beyond the committed feature set
The sidebar had only single-session deletion, which made stale or noisy
session lists expensive to maintain. This adds a batch-management lane in
the desktop UI and a server endpoint that deletes multiple sessions while
preserving per-session failure reporting and cleanup behavior.
Constraint: Session deletion must preserve existing transcript and adapter cleanup semantics
Constraint: Desktop UI should reuse the shared confirmation dialog instead of introducing a second modal surface
Rejected: Delete all selected sessions through repeated client DELETE calls | weaker partial-failure handling and duplicated cleanup orchestration
Rejected: Use a generic checkmark entry icon | it did not communicate batch deletion clearly enough
Confidence: high
Scope-risk: moderate
Directive: Keep batch deletion routed through the server batch endpoint so rollback and adapter cleanup stay centralized
Tested: bun test src/server/__tests__/sessions.test.ts -t batch-delete
Tested: bun run check:server
Tested: cd desktop && bun run check:desktop
Tested: browser UI smoke for group select, shift range select, delete confirmation, 7/30 day cleanup, Cmd+A filtered selection, and Escape exit
Not-tested: Live provider-backed session generation; this change covers session list management after sessions already exist
Long uninterrupted URLs can otherwise force user message bubbles beyond the chat column. The fix keeps the bubble constrained to its parent and allows safe breaks inside continuous URL text, with the same guard applied to markdown paragraphs and links.
Constraint: Issue #378 reports desktop chat overflow when a conversation contains a long URL
Rejected: Add horizontal scrolling to user messages | this preserves the overflow instead of fixing normal chat readability
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bunx vitest run src/components/chat/UserMessage.test.tsx src/components/chat/MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: bun run check:desktop
Tested: browser layout fixture PASS shell=619 bubbleClient=619 bubbleScroll=619
Not-tested: full root quality gate
Related: https://github.com/NanmiCoder/cc-haha/issues/378
This release packages the emergency H5 token pause so upgraded users can resume desktop and browser chat without generating or carrying a token. The release notes document the temporary open-access behavior and the verification run used before tagging.
Constraint: v0.2.4 restored startup but token auth can still break chat flows for upgraded users
Rejected: Wait for a full token-auth redesign | users are actively blocked and H5 auth is not critical right now
Confidence: high
Scope-risk: narrow
Directive: Restore H5 token controls only with migration coverage and browser chat E2E
Tested: bun run scripts/release.ts 0.2.5 --dry
Tested: bun run scripts/release.ts 0.2.5
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
The H5 token gate still caused chat startup and runtime failures after the desktop startup hotfix, so the default server path now stays open for browser and LAN access. Explicit auth remains available through SERVER_AUTH_REQUIRED=1 or --auth-required for deployments that intentionally need it.
Constraint: Current H5 token state is blocking active users from normal chat usage
Rejected: Keep automatic remote-host auth with broader client-side token handling | still leaves existing upgraded clients vulnerable to stale or missing token state
Confidence: high
Scope-risk: moderate
Directive: Do not re-enable default H5 token auth without a migration and end-to-end browser chat verification
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: cd desktop && bun run test src/lib/desktopRuntime.test.ts src/__tests__/generalSettings.test.tsx
Tested: bun run check:server
Tested: bun run check:desktop
This release moves the verified desktop H5-auth regression fix into the versioned artifact pipeline so blocked v0.2.3 users can receive a GitHub-built replacement quickly.
Constraint: GitHub Release body is sourced from release-notes/v0.2.4.md in the tagged commit.
Constraint: Release workflow requires the app, Tauri, Cargo, and lockfile versions to match the tag.
Rejected: Ship only the hotfix commit without a release tag | affected desktop users need packaged artifacts from the release workflow.
Confidence: high
Scope-risk: narrow
Directive: Keep v0.2.4 scoped to the startup auth regression; broader H5 improvements belong in a later release.
Tested: bun run scripts/release.ts 0.2.4 --dry
Tested: bun run scripts/release.ts 0.2.4
Not-tested: GitHub release workflow before push; it only runs after the tag is pushed.
The H5 LAN release made the desktop sidecar bind broadly and require auth for non-localhost hosts. The Tauri WebView still talks to the loopback control URL without an H5 bearer token, so startup requests were rejected before the app could load.
Constraint: Desktop WebView control requests must stay tokenless for the local sidecar.
Rejected: Reuse H5 token bootstrap for Tauri | would add remote-access state to the native app startup path.
Confidence: high
Scope-risk: narrow
Directive: Tauri WebView bypass is limited to loopback control hosts; do not extend it to LAN or public hosts without re-reviewing H5 auth boundaries.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts
Tested: bun run check:server
Browser-mode startup now respects an explicit Vite desktop server URL before falling back to same-origin H5 serving. This keeps local desktop smoke tests pointed at the real API server while preserving same-origin behavior for packaged H5 access.
Constraint: Vite dev smoke serves the web app and API on different loopback ports
Rejected: Treat same-origin as always preferred | Vite would answer /api requests with HTML and the chat surface never becomes ready
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- src/lib/desktopRuntime.test.ts src/api/client.test.ts --run
Tested: bun run check:desktop
Tested: bun run quality:gate --mode baseline --allow-live --only 'provider-smoke:*' --only 'desktop-smoke:*' --provider-model codingplan:main:codingplan-main
Not-tested: Full bun run quality:pr after this commit; it will run again during push
The release push gate exposed that the MessageList scroll tests fired synthetic user scrolls before the component's programmatic auto-scroll guard had reset. Waiting for the requestAnimationFrame reset keeps the tests aligned with the runtime behavior where users cannot scroll in the same frame as mount auto-scroll.
Constraint: Pre-push quality gate must pass before publishing the release tag
Rejected: Bypass pre-push verification | would publish without the repo's required PR quality evidence
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- src/components/chat/MessageList.test.tsx --run
Tested: bun run check:desktop
Tested: bun run check:coverage
Not-tested: Full bun run quality:pr after this commit; it will run again during push
The post-v0.2.2 changes are now summarized in the release body with the user-facing highlights first, while process-only H5 hardening is described as part of the new feature instead of old-version bug fixes. Desktop and Tauri version files are aligned to the release note version.
Constraint: GitHub Release body is sourced from release-notes/vX.Y.Z.md in the tagged commit
Constraint: Problem fixes should describe issues users could have seen in prior releases, not internal fixups for unreleased work
Rejected: Put every fix commit under 问题修复 | mixes development cleanup with released-user regressions
Confidence: high
Scope-risk: narrow
Tested: git diff --check
Tested: git diff --cached --check
Tested: bun run scripts/release.ts 0.2.3 --dry
Tested: version consistency check for desktop package, Tauri config, Cargo.toml, and Cargo.lock
Not-tested: Full bun run verify; release note and version bump only
The README is now the first screen most users see before deciding whether to download the app, so it leads with the desktop workflow, recent app features, and current screenshots while keeping CLI-from-source setup compact.
Constraint: Homepage should still mention the leaked Claude Code source origin.
Rejected: Keep the expanded CLI quick start on the homepage | it pushed sponsor and desktop content too far down.
Confidence: high
Scope-risk: narrow
Tested: bun run --silent check:docs
Tested: README image reference checks for Chinese and English files
Not-tested: GitHub README rendering after push
H5 exposes the desktop chat surfaces in a much narrower viewport, so the empty-session composer needs the same inset treatment as active sessions and generated diff rows need intrinsic width instead of compressing long code into the phone viewport.
Constraint: Keep desktop layout and short diff-row backgrounds unchanged while enabling sideways scrolling on H5.
Rejected: Force wrapping long diff lines | it makes code harder to inspect and diverges from desktop diff behavior.
Confidence: high
Scope-risk: narrow
Directive: Keep workspace diff rows content-width driven when adjusting mobile code preview styling.
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx --run
Tested: cd desktop && bun run test -- src/components/workspace/WorkspacePanel.test.tsx --run
Tested: cd desktop && bun run lint
Not-tested: Full bun run verify gate; change is scoped to desktop UI styling and covered by narrow Vitest plus typecheck.
The detached worktree fix corrects stale private-network H5 public URLs after the desktop server chooses a new ephemeral LAN port. Merge it into local main while leaving unrelated local release edits untouched.
Constraint: Local main already had unrelated unstaged desktop release metadata edits.
Rejected: Broad staging before the merge | would risk committing unrelated local release work.
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/h5-access-service.test.ts
Tested: bun run check:server
Not-tested: physical phone browser on local WiFi after merge
H5 access could keep returning a previously saved private-network URL even after the packaged desktop server moved to a new ephemeral LAN port. The public settings now prefer the current auto-detected LAN URL for local/private stored addresses while preserving explicit public reverse-proxy URLs.
Constraint: Packaged desktop H5 access uses an ephemeral server port and auto-publishes a LAN URL for phones on the same network.
Rejected: Always overwrite stored publicBaseUrl | public reverse-proxy deployments still need manual stable URLs.
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/h5-access-service.test.ts
Tested: bun test src/server/__tests__/h5-access-service.test.ts src/server/__tests__/h5-access-api.test.ts src/server/__tests__/h5-access-auth.test.ts
Tested: bun run check:server
Tested: manual LAN smoke with 0.0.0.0:5188 correcting stale 192.168.0.102:5179 to 192.168.0.102:5188
Not-tested: physical phone browser on local WiFi
The previous fix compared the venv's pyvenv.cfg `home` field with the
custom interpreter's directory, but Python's venv module skips the
wrapping venv when the source interpreter is itself in a venv (conda /
pyenv / hand-built venvs — which is the common case). The recorded
`home` then points to the base Python install, not the user-provided
path, causing the comparison to permanently fail and the UI to keep
showing "venv not ready" even right after a successful rebuild.
Switch to a marker file (~/.claude/.runtime/venv-base-interpreter.txt)
that records the exact `config.pythonPath` used at venv creation time.
Status check compares it verbatim with the current config. Falls back
to "match if both empty" when the marker is missing, which preserves
behavior for legacy users who never set a custom path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The dependency check ignored the custom Python path: it only verified
the global venv's stamp file, so switching to a fresh custom interpreter
still showed deps as installed even though the new interpreter's
environment was empty. Setup also reused the old venv built from a
different base interpreter.
Verify the existing venv via pyvenv.cfg's home field, but only when a
custom path is configured — users without a custom path keep the
original code path untouched, protecting existing installations.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Packaged desktop builds started the sidecar on loopback only, so phones on the same WiFi could not connect to the H5 URL. The server now binds to LAN interfaces while keeping the desktop control URL on loopback, serves the bundled H5 frontend, and reports a LAN public URL when H5 access is enabled.
Constraint: Packaged Tauri resources place ../dist under Contents/Resources/_up_/dist.
Rejected: Bind the desktop control URL to 0.0.0.0 | desktop clients should keep using loopback for local control.
Confidence: high
Scope-risk: moderate
Directive: Keep packaged H5 resource lookup aligned with Tauri resource mapping when changing bundle resources.
Tested: bun run check:server
Tested: cd desktop/src-tauri && cargo test --lib
Tested: cd desktop && bun run test -- desktopRuntime.test.ts generalSettings.test.tsx
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Packaged claude-sidecar smoke served H5 HTML while listening on *:39876
Not-tested: Physical phone browser test on the local WiFi network
- Skip startDragging() on right-click to prevent Tauri IPC from
disrupting the contextmenu event on macOS
- Allow context menu to render when sidebar is collapsed
- Fix auto-dismiss effect to not self-clear contextMenu 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>
Continuing an already materialized worktree session should behave like a normal existing session. The launch path now checks whether the session has already moved into its worktree before showing the startup bubble or passing worktree creation flags again.
Constraint: Existing session metadata can retain repository.worktree after the worktree transcript becomes the active session file
Rejected: Frontend-only suppression | the server could still launch the CLI with duplicate worktree creation flags
Confidence: high
Scope-risk: narrow
Tested: bun run check:server
Tested: bun test src/server/__tests__/conversations.test.ts -t worktree --timeout 30000
Tested: bun test src/server/__tests__/sessions.test.ts -t "newest duplicate session file" --timeout 30000
Not-tested: bun run verify still fails on unrelated desktop Settings.tsx and MessageList.tsx gates from the broader worktree
Programmatic scroll calls (scrollIntoView) trigger onScroll events mid-animation,
causing updateAutoScrollState to momentarily see a non-bottom position and flash
the button on/off. Guard against this with isProgrammaticScrollingRef.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mobile H5 shell was spending one row on the drawer button and another row on session metadata, which left less space for the actual chat on phones. The mobile shell now shares the drawer row with the active session title and metadata, while the session body suppresses its duplicate header on H5.
The mobile composer also restores horizontal breathing room and safe-area bottom padding so the input panel no longer sits flush against the viewport edge.
Constraint: Keep desktop/Tauri layout unchanged and scope the change to browser H5 mobile behavior.
Rejected: Keep the separate mobile menu row | it preserves the exact waste reported in phone screenshots.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce a second mobile session title row without checking a phone-sized H5 viewport.
Tested: cd desktop && bun run test src/components/layout/AppShell.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/ChatInput.test.tsx --run
Tested: git diff --check
Tested: agent-browser 393x852 H5 layout smoke with screenshot /tmp/h5-layout-verify/mobile-h5-layout.png
Not-tested: cd desktop && bun run lint, blocked by pre-existing Settings.tsx responseLanguage/responseLangDraft errors outside this change
Expose the CLI's `language` setting in the desktop app's General tab.
Users can now pick from 21 pre-defined languages via a dropdown (value
maps directly to CLI language names such as "chinese", "japanese").
Selecting a language writes it to ~/.claude/settings.json so the CLI
injects the language instruction into every session's system prompt.
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
Merge the H5 access branch into local main so the desktop app can expose an opt-in browser chat surface with token-gated remote access, mobile chat layout, and full-width bottom sheets for mobile pickers. The merge keeps desktop and Tauri behavior behind runtime checks while preserving local WebUI development bypasses for loopback and private-LAN server targets.
Constraint: H5 is opt-in and local browser development must not require a desktop-generated token
Rejected: Cherry-pick only the final UI commit | it omitted earlier auth, CORS, settings, and H5 token service dependencies
Confidence: high
Scope-risk: moderate
Directive: Keep mobile-only layout branches guarded by browser viewport plus non-Tauri runtime checks
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx src/__tests__/pages.test.tsx src/api/client.test.ts src/api/websocket.test.ts src/components/chat/ChatInput.test.tsx src/components/controls/PermissionModeSelector.test.tsx src/components/layout/AppShell.test.tsx src/components/layout/Sidebar.test.tsx src/components/shared/RepositoryLaunchControls.test.tsx src/hooks/useMobileViewport.test.tsx src/lib/desktopRuntime.test.ts src/pages/ActiveSession.test.tsx src/pages/EmptySession.test.tsx src/stores/settingsStore.test.ts
Tested: bun test src/server/__tests__/h5-access-api.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-service.test.ts src/server/middleware/cors.test.ts
Tested: git diff --check HEAD^ HEAD
Not-tested: bun run verify full quality gate
Not-tested: mobile Chrome smoke was not rerun after merging into local main
H5 browser use needs to keep local desktop WebUI testing open while giving remote phones a usable chat-first surface. This change scopes browser auth by origin and target host, hides non-chat desktop chrome on H5 mobile, compacts the empty-session composer, and unifies mobile selectors behind a full-width bottom sheet with an explicit close action.
Constraint: Local browser development may bypass H5 auth only for loopback or private LAN server hosts; public or remote hosts still require an H5 token.
Constraint: Mobile H5 scope is chat-first; settings and scheduled-task surfaces stay hidden instead of fully redesigned.
Rejected: Reuse desktop popovers on mobile | hover/anchored dropdown behavior leaves key actions hard to dismiss and cramped on narrow screens.
Rejected: Let any localhost origin bypass remote auth | it would let arbitrary local pages access public H5 servers without a token.
Confidence: medium
Scope-risk: moderate
Directive: Keep future H5 selector popups on MobileBottomSheet unless a selector needs a reviewed custom mobile interaction.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx src/lib/desktopRuntime.test.ts src/components/shared/RepositoryLaunchControls.test.tsx src/components/controls/PermissionModeSelector.test.tsx src/components/controls/ModelSelector.test.tsx src/components/shared/DirectoryPicker.test.tsx src/__tests__/pages.test.tsx
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: Chrome mobile viewport smoke for context, model, permission, project, branch, and worktree bottom sheets with no horizontal overflow.
Not-tested: Full bun run verify quality gate.
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
The desktop docs now explain the intended H5 setup path for personal and team use: enable H5 in Settings, generate a one-time token, configure allowed origins, and use LAN or a reverse proxy to open the mobile browser chat surface.
Constraint: H5 is opt-in browser access, not a public multi-tenant auth system.
Rejected: Hide the setup details in the implementation spec only | users need operational guidance for token handling, CORS, and reverse proxy routing.
Confidence: high
Scope-risk: narrow
Directive: Keep this page aligned with Settings labels and the token/CORS behavior before documenting broader public hosting.
Tested: bun run check:docs
Tested: Live local smoke with temporary HOME: /health 200, H5 verify without token 401, H5 verify with token 200, configured Origin echoed by CORS.
The browser H5 surface now switches to a phone-oriented shell: the sidebar becomes a closed-by-default drawer, chat stays primary, workspace and terminal panels stay off the mobile chat surface, composer controls use larger touch targets, and mobile menus avoid desktop-only widths and keyboard hints. Desktop and Tauri sidebar behavior remain on the existing store-driven path.
Constraint: H5 is personal/team browser access layered on the existing desktop web UI, so the normal desktop app must keep its current layout behavior.
Rejected: Share the global sidebarOpen default for mobile first paint | it can flash the drawer open before effects run on a phone.
Confidence: high
Scope-risk: moderate
Directive: Keep mobile-only layout branching behind useMobileViewport() && !isTauriRuntime() unless a future task explicitly redesigns the native desktop shell.
Tested: cd desktop && bunx vitest run src/hooks/useMobileViewport.test.tsx src/components/layout/AppShell.test.tsx src/components/layout/Sidebar.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/ChatInput.test.tsx src/components/controls/PermissionModeSelector.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Browser H5 startup now binds the in-memory bearer token only when the selected server URL is non-loopback, so stored remote credentials cannot follow a later localhost, 127.0.0.1, or IPv6 loopback startup. Token verification errors also use the structured HTTP status when available instead of relying only on message text.
Constraint: Browser H5 may reuse localStorage across remote and local server URLs.
Rejected: Clear all remembered H5 connection state on every loopback startup | preserving the remote URL is useful for the next remote retry and the token alone is the leak risk.
Confidence: high
Scope-risk: narrow
Directive: Keep browser auth token assignment gated by requiresH5AuthForServerUrl before adding new bootstrap paths.
Tested: cd desktop && bunx vitest run src/api/client.test.ts src/api/websocket.test.ts src/lib/desktopRuntime.test.ts src/components/layout/AppShell.test.tsx
Remote browser bootstrap now keeps reverse-proxy path prefixes, recognizes IPv6 loopback as local, and converts remote health or verify failures into recoverable H5 connection prompts instead of falling back to the generic startup error surface. Token invalidation clears only the stored token so users can retry without re-entering the server address.
Constraint: Review fixes must stay inside the existing Task 4 browser-runtime scope without changing the Tauri localhost startup path
Rejected: Clear both stored server URL and token on remote bootstrap failures | it forces unnecessary re-entry after stale credentials
Directive: Any future browser-mode startup error for non-loopback backends should remain recoverable through H5ConnectionView unless the desktop/Tauri contract changes
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bunx vitest run src/api/client.test.ts src/api/websocket.test.ts src/lib/desktopRuntime.test.ts src/components/layout/AppShell.test.tsx
Tested: cd desktop && bun run lint
Tested: git diff --check -- desktop/src/api/client.ts desktop/src/api/websocket.ts desktop/src/lib/desktopRuntime.ts desktop/src/components/layout/H5ConnectionView.tsx desktop/src/components/layout/AppShell.tsx desktop/src/api/client.test.ts desktop/src/api/websocket.test.ts desktop/src/lib/desktopRuntime.test.ts desktop/src/components/layout/AppShell.test.tsx
Not-tested: Live reverse-proxy browser session against a real remote H5 server
Browser-mode startup now preserves the desktop localhost path while prompting for H5 credentials when a non-loopback backend requires verification. The API client and websocket layer share a central token source so REST and session sockets authenticate consistently without leaking the token into diagnostics.
Constraint: Preserve the Tauri startup path and default localhost browser behavior
Rejected: Reuse the generic startup error view for H5 auth failures | it blocks the credential-entry recovery path
Directive: Non-loopback browser startup is the only path that should require saved H5 verification without re-checking the desktop bootstrap contract
Confidence: high
Scope-risk: moderate
Tested: cd desktop && bunx vitest run src/api/client.test.ts src/api/websocket.test.ts src/components/layout/AppShell.test.tsx
Tested: cd desktop && bun run lint
Tested: git diff --check -- desktop/src/api/client.ts desktop/src/api/websocket.ts desktop/src/lib/desktopRuntime.ts desktop/src/components/layout/H5ConnectionView.tsx desktop/src/components/layout/AppShell.tsx desktop/src/api/client.test.ts desktop/src/api/websocket.test.ts desktop/src/components/layout/AppShell.test.tsx
Not-tested: Live remote H5 browser session against a running server