The proxy URL written into client configs (including Claude Desktop's gateway URL) was always derived from the listen address, with 0.0.0.0 written as 127.0.0.1, so clients outside the host's loopback, such as Claude Desktop sessions in WSL2 NAT mode, got an unreachable address.
Add an optional per-device "connection address" in Settings > Routing, stored in ~/.cc-switch/settings.json so cloud sync never carries it to another machine. When set it replaces the host in every proxy URL written to clients; saving rewrites routed clients without restarting the service and is safe to retry.
Fixes#7877
- Reserve the scrollbar gutter with overflow-y: scroll instead of scrollbar-gutter: stable, which WKWebView ignores for custom ::-webkit-scrollbar styles. Switching usage tabs no longer changes the content width and re-picks the chart's x-axis ticks.
- Slide SegmentThumb with left/top instead of transform, so WebKit no longer promotes the buttons above it to compositing layers and the icons stop jittering by half a pixel.
The header row already reserved space for a checkbox column but had none,
so selecting every Skill meant ticking each row by hand.
- The box acts on the rows the current search and filters show. It is
checked when all of them are selected, indeterminate when some are, and
clicking it while unchecked or indeterminate selects the rest.
- Selections outside the current filter are left alone.
* feat(logging): log provider switches and routing / aggregation mode changes
Switching providers and entering or leaving the routing / aggregation
modes left nothing in cc-switch.log, so "I clicked switch and nothing
happened" reports could not be told apart from a rule refusal, a user
cancel or a pending roll-forward.
- [SWITCH]: every ProviderService::switch logs where it went (direct
from -> to, or proxy route from -> to) and how it ended: done, done
with warnings, refused by the official-provider rule (INFO), or failed
(ERROR).
- [MODE]: enter / attach, exit / detach, route changes and the direct-mode
route memory log the mode transition (direct / routing / aggregation),
the route or direct provider, and whether the client config was
rewritten; failures log at ERROR.
- Frontend: the mode dialogs log when they open and what the user picked
(cancel, switch directly anyway, start routing, confirm with a pick),
and the frontend-only switch gates log too.
Fixes#7881
* fix(logging): drop quoted TOML source lines from switch / mode failure logs
TOML parse diagnostics quote the offending config line, which can be the
one holding a credential. Strip the quoted source lines before the new
backend failure logs write them to cc-switch.log; line/column and the
reason are kept.
The active start/end card drew an orange ring around its own gray border, so a gray box showed inside the orange outline. Use border-ring so the card has a single orange outline, and add focus:ring-0 to the date and time inputs so clicking into them no longer adds another ring inside the card. Add 12px of space between the preset row and the fields below it.
The bottom bar's border-t ran edge to edge and sat flush against the last
global item, so a selected Usage row touched the line. Draw it as an inset
divider with 6px above and below, like the divider above the global items,
and drop the extra gap between Apps and Settings on the collapsed rail.
Lay out the sidebar and content area at their final widths once, then draw the moving edge with a cover panel that slides on a 200 ms transform animation, so the toggle runs at the display's refresh rate instead of WKWebView's ~60fps main-thread rate. Interrupting a toggle continues from the current edge; window-resize auto-collapse and prefers-reduced-motion skip the animation. Drop the width transition and the content-width pinning from #7845. <main> gets an opaque bg-app and isolate so its z-indexed descendants (sticky matrix headers, FullScreenPanel) stay under the cover while collapsing.
Newer Gemini CLI releases record chats as tmp/<hash>/chats/session-*.jsonl and migrate legacy .json files to .jsonl on resume, so the Sessions page showed no Gemini sessions and Gemini usage sync imported nothing from them.
- Replay JSONL like Gemini CLI's loadConversationRecord (in-place upsert by id, $set and $set.messages checkpoints, $rewindTo), with an id index so replay stays linear
- Use the shared parser for the session list, message loader, full-content refs and usage sync
- Hide a legacy .json that sits beside its migrated .jsonl and delete both together, removing the legacy file first
- Skip CLI-injected <session_context>/<hook_context> and slash commands when deriving titles, and mark injected context in the message view
Fixes#7861
Move the collapsed state and the Cmd/Ctrl+\ shortcut from App into Sidebar so a toggle only re-renders the sidebar. During the 200 ms width transition, pin the content area's children to the collapsed-sidebar width so ResizeObserver-driven content (usage chart, virtual lists, tab indicators) lays out once at the end instead of every frame; skipped under prefers-reduced-motion. useSlidingIndicator no longer sets state when the measured rect is unchanged.
When a Responses SSE stream delivers its output only in the terminal response.completed / response.incomplete event, route it through the established Responses JSON conversion so text before a web search stays ahead of the search result. A missing terminal status falls back by event name, matching the incremental path's stop reason mapping. Also fixes terminal-only function_call items that previously produced stop_reason tool_use without a tool_use block.
Follow-up to #5681.
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
* ci: add WSL2 backend checks
* ci: fix WSL2 backend job by keeping MSVC toolchain on native temp
- compile test binaries before pointing TEMP/TMP at the WSL2 UNC path
(mt.exe fails with c1010070/LNK1327 when TEMP is a UNC path)
- inject WSL2 TEMP/TMP per test step instead of via GITHUB_ENV
- use --tests so doctests cannot trigger link steps under the UNC temp
- run the filesystem contract gate before the full serial suite
- drop fmt/clippy steps duplicated from the existing Windows job
* ci: move full WSL2 suite to nightly, keep per-PR contract gate
- the full serial suite needs 50+ minutes on the 9P filesystem
(2026/2343 lib tests finished in 39.5min before the 45min timeout)
- per-PR job now runs only the atomic-write contract (~6min total)
and builds just the lib test binary via --lib
- new nightly workflow (schedule + workflow_dispatch, 90min timeout)
runs the contract plus the full serial suite on the WSL2 home
---------
Co-authored-by: Jason <farion1231@gmail.com>
* fix(windows): fall back when WSL rejects atomic replace
* fix(windows): preserve config during WSL fallback
* fix(windows): use rename fallback for WSL UNC
* fix(opencode): support unified OMO config
* fix(opencode): couple rollback snapshots to writes
* fix(ci): simplify rollback version type
* fix OMO unified config review issues
* validate OMO round-trip semantics
* feat(opencode): load OMO models from runtime opencode models
Surface OAuth/Zen free models in OMO/OMO Slim pickers by running the
installed OpenCode CLI, with a 20s timeout and a toast when discovery fails.
* style: fix rustfmt and prettier for OMO runtime models
* fix(opencode): pass OPENCODE_CONFIG_DIR for runtime model discovery
Honor the configured OpenCode config directory when running
`opencode models`, including WSL UNC path translation. Also drop the
unused run_detected_tool_command wrapper that failed clippy.
* fix(opencode): satisfy Windows Clippy
* fix(opencode): bound runtime model discovery
* fix(opencode): address runtime model review feedback
* fix(ci): parenthesize unsafe kill expression
* fix: sync openclaw live provider updates
* fix: sync hermes live provider updates
* fix test: hermes live import stores models as array after denormalize
The test import_hermes_providers_from_live_updates_existing_provider_from_live
seeded models as a dict, but import_hermes_providers_from_live reads via
get_providers() which calls denormalize_provider_models_for_read(), converting
models from YAML dict to UI-friendly array. The test assertion accessed models
as dict -> Null -> assertion failure.
Fix: access models as array by index, matching the actual storage format after
live import. Also verify the id field is preserved in the denormalized output.
* fix: sync existing opencode providers from live config
* fix: preserve OpenCode provider display names
* docs: update comments and log messages to reflect new update behavior
- fix stale comment in lib.rs that said existing providers are skipped
- change log message from 'Imported' to 'Synced' since count now
includes both new imports and updates