* [AI] fix(agents): classify HTTP 429 overloaded bodies as overloaded, not rate_limit
Reorder overloaded check before rate_limit across all three classification paths:
- classifyFailoverClassificationFromHttpStatus (HTTP status path)
- classifyFailoverClassificationFromMessage (message text path)
- formatRateLimitOrOverloadedErrorCopy (user-facing copy path)
Previously, isRateLimitErrorMessage matched the bare '429' token in
overloaded-worded 429 bodies (e.g. z.ai code 1305), misclassifying them
as rate_limit and showing the wrong user-facing error message. 503/499
already check overloaded before defaulting; 429 now follows the same pattern.
Related to #98101
* [AI] fix(agents): put MODEL_CAPACITY_ERROR_RE before overloaded in user-facing copy
MODEL_CAPACITY_ERROR_RE is the most specific overloaded variant
('selected model is at capacity') and must be checked before the
broader isOverloadedErrorMessage matcher (which also covers model at
capacity), otherwise it's shadowed with a less specific message.
Related to #98101
* fix(agents): preserve overload copy for rate-limited responses
* fix(agents): retain provider retry guidance
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(feishu): avoid forwarding mentions without bot open id
* fix(feishu): require bot identity for mention forwarding
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auth): preserve auth profile order when copying store to spawned agent
* fix(auth): preserve copied profile order
Co-authored-by: Vivek Behani <vivekbehani@me.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(openai): stop double-prefixing SSE bodies mislabeled as JSON
OpenAI-compatible gateways that stream real SSE (data: {...}) but label the
response application/json hit the streaming JSON-wrap fallback, which re-prefixed
each frame as 'data: data: {...}' and broke JSON.parse in the OpenAI SDK. Sniff
JSON-labeled streaming bodies and relabel genuine SSE as text/event-stream so the
SSE sanitizer parses them verbatim.
* test(openai): prove mislabeled SSE stays streaming
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(crestodian): suppress unhandled stdout/stderr stream errors in probeLocalCommand
Add no-op error listeners on child stdout/stderr in probeLocalCommand
to prevent unhandled EventEmitter errors from crashing the process.
Replace standalone proof scripts with Vitest regression test that verifies
error listeners are registered on the production code path.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(crestodian): exercise probe stream failures
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): redesign sessions page into roster table with details drawer
* fix(ui): decouple sessions drawer expansion from deep-link query narrowing; update mobile column hiding for new roster
* fix(ui): reload open session drawer when a session gains compaction checkpoints
* fix(browser): read Windows Chrome version metadata
Read PE ProductVersion before a conservative unambiguous install-layout fallback, without interpolating configured paths into PowerShell code.\n\nCo-authored-by: Mukunda Rao Katta <mukunda.vjcs6@gmail.com>
* style(browser): clarify Windows version probe
* fix(browser): use trusted Windows PowerShell path
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(android): support per-gateway custom headers for proxied gateway connections
Per-gateway custom request headers (Cloudflare Access-style service tokens)
for gateways behind authenticating reverse proxies. Headers are credentials:
EncryptedSharedPreferences-stored per endpoint stableId, masked in the manual
connection UI, injected on the OkHttp WebSocket upgrade at connect time via a
provider so edits apply on the next reconnect without re-pairing. Reserved
handshake headers and non-printable-ASCII are sanitized out (OkHttp throws on
them). TLS fingerprint pinning and pairing flows unchanged. Cross-gateway
isolation proven by MockWebServer upgrade-capture test.
Related: #100698
* chore(android): sync native i18n for custom header strings
* fix(android): harden gateway custom headers
* chore(android): resync native i18n inventory
* docs(changelog): defer Android release note
* fix(android): validate custom header names
* chore(android): refresh native i18n inventory
* feat(ui): link file paths in chat and add workspace file viewer sidebar
Auto-detect file paths in chat markdown (plain paths, path:line, inline
code, explicit/absolute links) and render them as clickable links that
open a dedicated file viewer sidebar with line numbers, highlight.js
syntax highlighting, scroll-to-line, in-file search, reveal-in-rail,
open-in-editor, and copy actions. Workspace rail browser entries are
now previewable even when the transcript never touched them;
sessions.files.get serves any file inside the workspace root with the
existing containment, symlink/hardlink, and size guards.
Closes#100615
* feat(ui): linkify file paths in sidebar markdown and fix file-view gutter width
Markdown rendered in the chat detail sidebar now gets the same clickable
file links as chat messages, opening files via the panel's own click
delegation. File viewer gutter uses a fixed per-file digit width so
short lines no longer indent their code past longer ones.
* fix(ui): keep browser-entry active highlight and narrow file content for editor open
loadWorkspace no longer clears the rail active marker for browser-only
entries, so Show in Files keeps the revealed file highlighted after the
forced reload. openInEditor narrows the sidebar content kind before use,
fixing the tsgo type error.
* chore(ui): fix lint findings in file viewer helpers
* fix(ui): harden workspace file previews
* fix(ui): wire file previews through chat panes
* fix(ui): preserve workspace file targets
* fix(ui): preserve workspace file targets
searchMemoryWiki called sharedMemoryManager.search() and getMemoryWikiPage
called manager.readFile() with only a truthiness guard. A memory plugin
whose runtime returns a manager that does not implement the
MemorySearchManager contract crashed shared-backend wiki search with
"sharedMemoryManager.search is not a function" from inside the bundle --
@mem0/openclaw-mem0 <= 1.0.14 registered exactly such a partial manager
(status/probeEmbeddingAvailability/close only).
Guard both call sites and throw an actionable error instead: name the
missing contract method and point at search.backend="local" for wiki-only
access.
* fix(gateway): stop device pairing approval alert floods from retrying devices
A device retrying with a broken token minted a new pending requestId (and
approval alert broadcast) every 5-minute TTL window because refresh preserved
ts and expiry keyed on ts. Pending requests now stay alive via an internal
refreshedAtMs keepalive (ts still owns ordering/--latest), superseded device
requests broadcast device.pair.resolved like node pairing already did, and the
Mac app keeps one alert per device, closes superseded visible alerts, and
resyncs its queue when approving a stale request.
Closes#100974
* chore: satisfy swiftformat and refresh native i18n inventory for pairing prompter changes
* feat(ios): support per-gateway custom headers for proxied gateway connections
Per-gateway custom request headers (Cloudflare Access-style service tokens)
for gateways behind authenticating reverse proxies. Headers are credentials:
Keychain-stored per gateway stableID, masked in settings UI, injected on the
WebSocket upgrade at connect time via a provider so edits apply on the next
reconnect without re-pairing. Reserved handshake headers and control chars
are sanitized out. TLS pinning and pairing flows unchanged. Push relay and
other non-gateway destinations proven header-free by regression test.
Related: #100698
* chore(ios): sync native i18n inventory for custom header strings
* fix(ios): harden gateway custom headers
* chore(ios): resync native i18n inventory
* fix(ios): validate custom header names