## Fixes
- Codex: auto-retry when upstream drops mid-stream (no more hangs)
- Codex: fix random 400/404 errors, tool-calling failures, and unstable prompt cache
- MITM: support Antigravity 2.x (updated IDE version detection and DNS/cert flow)
- Sanitize Read tool args to prevent retry loops from non-Anthropic models (#1144)
- Implement json_schema fallback for OpenAI-compatible providers without native Structured Output (#1343)
- Strip empty Read pages argument in OpenAI-to-Claude translator (#1354)
- Forward Gemini output dimensions for embeddings (#1366)
- Resolve setState-in-effect errors in dashboard components (#1362)
- Gemini CLI: reuse stored OAuth project IDs for quota checks and show clearer setup guidance when the project is missing (#1271, #1428)
- LanguageSwitcher: remove mounted state + useLayoutEffect pattern
Portal renders directly based on open state (SSR-safe without client check)
- UsageStats: replace stats-null check with isInitialLoad ref to avoid
setState in effect body (cascading render issue)
commit 6561679f5c396bb07f5f7ba5bc5ec75e81c803a4
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:26:01 2026 -0700
fix: never dedup access_token connections
Access tokens should always create new entries. User decides
which to keep (refresh-based OAuth vs no-expiry website token)
and removes the other manually.
commit d773451657999a2965ca4a094a7f0b7a54066693
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:24:30 2026 -0700
fix: support ChatGPT website token format (account_id, plan_type)
ChatGPT website access tokens use top-level 'account_id' and
'plan_type' fields, while OAuth id_tokens use nested claims
under 'https://api.openai.com/auth'. Now both formats are
handled, so workspace dedup works for website tokens too.
commit cb895a5f6be59c51267874f11567646fa1f43016
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:12:56 2026 -0700
fix: detect JWT in manual callback URL field
When user pastes a JWT access token (starts with eyJ) in the
'paste callback URL' input field, skip URL parsing and send
it directly to the exchange endpoint as the code. Fixes
'Failed to construct URL: Invalid URL' error.
commit 29650d4a6732e3cf0958c9963b53209e41c8281e
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 15:37:02 2026 -0700
feat: auto-detect access token in OAuth exchange
When the exchange endpoint receives a JWT (starts with eyJ)
instead of an OAuth authorization code, it detects this and
creates an access_token connection directly — skipping the
OAuth token exchange flow.
This lets users paste a ChatGPT access token where the OAuth
code would normally go, and have it work automatically.
commit e8e7c5709a783abd0c45246a44de1cc6abdba100
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 15:14:48 2026 -0700
feat: workspace-aware dedup + ChatGPT access token import
1. Dedup now checks email AND workspace (chatgptAccountId)
- Same email in different workspaces = separate connections
- Backward compatible: non-workspace providers still dedup by email
2. New authType 'access_token' for ChatGPT website tokens
- POST /api/oauth/codex/import-token accepts raw access tokens
- Extracts email, workspace, plan from JWT claims
- Deduplicates by email+workspace like OAuth
- No refresh token needed (avoids OAuth relogin issues)
## Features
- Xiaomi MiMo Token Plan: region selector (Singapore / China / Europe) — keys are cluster-specific
- Antigravity: risk confirmation dialog before first connection
- Gemini CLI: surface upstream retry delay on 429 errors
## Fixes
- MITM: cannot kill process on macOS under sudo (lsof not found in PATH)
- Stream: false-positive stall timeout on Claude reasoning / Kiro responses
- Tunnel: cannot re-enable after disable (stuck state)
- Tunnel: cloudflared error messages now include log tail for easier debugging
- Language switcher: applies selected locale immediately on close (#1234)
- Antigravity OAuth: metadata now matches the official client
## Improvements
- Gemini CLI: bump engine to 0.34.0
- Re-hide `qwen` (OAuth EOL) and `iflow` (not ready) providers
Remove `hidden: true` so the providers appear in the UI list while
keeping their existing deprecation/risk notices.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fixes#1226
The Antigravity OAuth flow sent inconsistent client metadata between
the token acquisition phase and the API usage phase. String enum values
(IDE_UNSPECIFIED, PLATFORM_UNSPECIFIED) were used during OAuth token
exchange + loadCodeAssist + onboardUser, while numeric enums (ideType: 9,
platform: <computed>, pluginType: 2) were used in runtime API calls.
Google detected this fingerprint mismatch and blocked 9router accounts.
Replace all string enum occurrences with the correct numeric values:
- src/lib/oauth/constants/oauth.js: loadCodeAssistClientMetadata now
uses getOAuthPlatformEnum() for platform and numeric 9/2 for
ideType/pluginType, matching getOAuthClientMetadata()
- src/lib/oauth/services/antigravity.js: getMetadata() now delegates
to getOAuthClientMetadata() instead of returning hardcoded strings
- src/lib/oauth/providers.js: postExchange metadata now uses
getOAuthClientMetadata() instead of inline string enums
- open-sse/services/usage.js: getGeminiSubscriptionInfo body now uses
CLIENT_METADATA (already imported from appConstants.js) instead of
inline string enums
## Features
- Add Vercel AI Gateway provider support (#1183)
- rtk: Kiro format tool result compression — handle conversationState.history & currentMessage, preserve error results, ~13.6% savings (#1194)
## Fixes
- openclaw: normalize agent.model object form `{primary, fallbacks}` before .startsWith → fix TypeError & 'not configured' status (#1216)
- Usage Details pagination: stay inside mobile viewport <640px (#1218)
- Fix test model error
- Fix MIMO provider in Codex
- Disable log file creation when using MITM AG
OpenClaw 2026.5.x writes agents[].model as either a plain string
or as an object { primary, fallbacks }. The status enrichment and
POST cleanup both called .startsWith() on the raw value, which threw
TypeError when the object form was present and made the dashboard
report 'not configured'.
Add a resolveAgentModel helper that accepts both shapes and returns
the string id (model.primary for the object form, empty string for
missing/invalid). Use it when enriching agents in GET so consumers
receive a string model field, and when filtering the list in POST.
Refs decolua/9router#1196
On viewports below 640px the pagination row at Dashboard > Usage >
Details rendered the rows-per-page select plus the full numbered page
list plus the prev/next chevrons in one unwrapped flex row with no
horizontal padding. On a 390px phone the Rows: label was clipped on
the left and the next chevron was clipped on the right.
Add px-2 to the outer container so the pagination card has horizontal
breathing room. Switch the inner controls group to flex-wrap with
gap-2 on mobile (and gap-4 from sm: up) so the rows-per-page select
can wrap below the chevrons. Hide the numbered page buttons, the
1 / last anchors, and the ... ellipses below sm: -- only the prev,
current page indicator, and next chevrons render on mobile, which
keeps every control reachable while fitting inside the card.
Desktop layout (>= 640px) is unchanged.
Refs decolua/9router#1146
- Fix duplicate tray icon on macOS when hiding to tray
- Fix tray not showing in background mode on macOS
- Fix hide to tray broken on Windows/Linux
- Fix Shutdown button in web UI not working
## Features
- Add Blackbox provider with `bb` alias (#1143)
- Add Xiaomi token plan provider
- Enhance model select modal UX + modal traffic lights (#1111)
- Default Usage dashboard period to Today (#1141)
## Fixes
- Fix Cowork model selection and Windows CLI packaging (#1129)
- Update provider name retrieval for compatibility provider (#1135)
- Update JWT_SECRET handling
Cherry-picked from upstream PR #1129 + local improvements:
- dedupe inline remove-model handler -> use handleRemoveModel
- add .next-cli-build/ and cli/.build-home/ to .gitignore
* feat(model-select-modal): highlight added models and support bulk selection
- Add addedModelValues prop to highlight already-added models with primary color
- Sort models alphabetically per provider, with added models floated to top
- Replace green highlight with primary brand color (orange #E56A4A)
- Use check icon (10px) inline with model name instead of check_circle
- Replace Done button with info bar explaining click-to-toggle behavior
- Add ProviderIcon to provider group headers replacing colored dot
- Import ProviderIcon, remove unused Button import
* feat(cli-tools): wire addedModelValues, onDeselect, and auto-save to model select modals
- Pass selectedModels as addedModelValues to ModelSelectModal in OpenCode and Copilot cards
- Add onDeselect handler to remove model from list on second click
- Set closeOnSelect=false to allow bulk model selection
- Remove manual setModalOpen(false) from onSelect callbacks
- Add saveModels() silent auto-save triggered on modal close (OpenCodeToolCard)
- Use useRef to track latest selectedModels in closure-safe way
* feat(modal): functional traffic light close button with hover icon and tooltip
- Make red dot a clickable button that closes the modal
- Show ✕ icon inside red dot on hover via group-hover opacity transition
- Gray out yellow and green dots (cursor-not-allowed, no tooltip)
- Increase dot size from w-3 h-3 to w-4 h-4
- Add Tooltip with brand-matched color #FF5F56 on red dot
- Remove X close button from modal header
* feat(tooltip): add color prop for themed tooltip backgrounds
* feat(i18n): add translations for model select info bar and close tooltip
- Add 'Click to add, click again to remove. Changes are saved automatically.' to all 32 locales
- Add 'Close' translation to all 32 locales
* fix(ui): address code review feedback on modal UX and auto-save
- Modal: remove showCloseButton prop, use showTrafficLights for header
condition, hide traffic lights on mobile (hidden md:flex), add mobile
X button (md:hidden) with aria-label, add aria-label and title on
traffic light close button
- OpenCodeToolCard: validate activeModel membership before saving —
fallback to models[0] or empty string; clear/reassign activeModel
on deselect when removed model was the active one
- CopilotToolCard: add useRef + selectedModelsRef, add saveModels()
using /api/cli-tools/copilot-settings, wire auto-save on modal close
- ModelSelectModal: fix JSX formatting — separate info bar closing div
from Search comment onto its own line
* feat(usage): add Today period option to Usage & Analytics
Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).
Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại
Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
+ getUsageStats: dùng nhánh live history khi period = today,
cutoff lấy từ 00:00 hôm nay theo local time.
+ getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.
* feat(usage): đặt mặc định period là Today khi mở dashboard/usage
Người dùng thường quan tâm usage trong ngày hôm nay nhiều hơn 7 ngày,
nên hiển thị Today ngay khi vừa mở trang để giảm 1 thao tác chuyển period.
Đồng bộ luôn fallback trong UsageStats để giữ nhất quán khi component
dùng standalone.
The visibility, copy, and delete buttons in the API Keys list used , which never reveals on touch devices because they don't fire :hover. Switch all three to — visible by default on mobile, hover-revealed on sm+ — matching the pattern already used in providers/ and media-providers/
Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>
Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).
Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại
Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
+ getUsageStats: dùng nhánh live history khi period = today,
cutoff lấy từ 00:00 hôm nay theo local time.
+ getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.